Java 字符串一旦被创建出来就会一直存在于字符串常量池中?直到Jvm停止运行?



volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

1)、存在层次 Java的关键字,在jvm层面上
2)、锁的释放 @1以获取锁的线程执行完同步代码,释放锁 @2线程执行发生异常,jvm会让线程释放锁
3)、锁的获取 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待
4)、锁状态 无法判断
5)、锁类型 可重入 不可中断 非公平
1)、存在层次 是一个类
2)、锁的释放 在finally中必须释放锁,不然容易造成线程死锁
3)、锁的获取 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
4)、锁状态 可以判断
5)、锁类型 可重入 可判断 可公平(两者皆可)

 这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。
 这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。
Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。
 1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
 2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
 3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。可以对基本数据、数组中的基本数据、对类中的基本数据进行操作。原子变量类相当于一种泛化的volatile变量,能够支持原子的和有条件的读-改-写操作。—— 当前的处理器基本都支持CAS,只不过每个厂家所实现的算法并不一样罢了,每一个CAS操作过程都包含三个运算符:一个内存地址V,一个期望的值A和一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。CAS的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取、调用对象方法的功能称为java语言的反射机制。
反射获取对象的三种方式:
//此方法含义是:加载参数指定的类,并且初始化它。

58.什么是java序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。
可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,
implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流
要恢复的话则用输入流。
序列化:序列化是将对象转换为容易传输的格式的过程。
例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。
在另一端,反序列化将从该流重新构造对象。
是对象永久化的一种机制。
确切的说应该是对象的序列化,一般程序在运行时,产生对象,
这些对象随着程序的停止运行而消失,但如果我们想把某些对象
(因为是对象,所以有各自不同的特性)保存下来,在程序终止运行后,这些对象仍然存在,
可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存下来的对象。这种情况下就要用到对象的序列化。
对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整性和可传递性。
譬如通过网络传输,或者把一个对象保存成一个文件的时候,要实现序列化接口 。

59.动态代理是什么,有哪些应用?

动态代理是实现对接口中全部的方法进行统一的拦截,不需要在每一处代理方法中添加拦截内容。
动态代理是AOP的一种实现。
AOP(AspectOrientedProgramming):将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码—解耦。

60.怎么实现动态代理?

61.为什么要使用克隆?

克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠clone方法了。
所以我们需要能够实现对于输入的实参进行了一份拷贝,若方法参数为基本类型,则在栈内存中开辟新的空间,所有的方法体内部的操作都是针对这个拷贝的操作,并不会影响原来输入实参的值 。若方法参数为引用类型,该拷贝与输入实参指向了同一个对象,方法体内部对于对象的操作,都是针对的同一个对象。

62.如何实现对象克隆?

2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆;

63.深拷贝和浅拷贝区别是什么?

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

相通点:servlet是java的 we工程,内部实现接受请求和进行请求响应的方式,servlet就相当于一台服务器,接受请求和提供服务,而jsp其实就是一个servlet,只是是多种servlet实现中的一种而已。
区别:servlet是在服务器端运行返回html等静态页面,而servlet时运行在web容器里面,比如tomcat,通过服务器返回数据到jsp界面,然后容器通过jvm把jsp界面编辑成.java和.class文件,然后执行生成html界面。

65.jsp有哪些内置对象作用是什么?

在实际的Web应用开发过程会经常遇到这样的一个问题:会话状态的维持。当然有很多种方法可以用来解决这个问题,例如:Cookies、隐藏的表单输入域或者将状态信息直接附加到URL当中去,但是这些方法使用非常不便。
Java Servlet提供了一个可以在多个请求之间持续有效的会话对象HttpSession,此对象允许用户存储和提取会话状态的信息。JSP同样也支持了Servlet中的这个概念。JSP中的session内置对象就是对应于Servlet中的HttpSession对象。当Web应用系统希望通过多个页面完成一个事务的时候,session的使用是非常有用和方便的。
它的作用有点类似于前一节介绍的session内置对象。但是它们之间还是有区别的,一般来说,一个用户对应着一个session,并且随着用户的离开session中的信息也会消失,所以不同客户之间的会话必须要确保某一时刻至少有一个客户没有终止会话;而application则不同,它会一直存在,类似于系统的“全局变量”,而且只有一个实例。
exception内置对象是用来处理页面出现的异常错误,它是java.lang.Throwable类的一个对象。前面已经讲过,在实际JSP网站开发过程中,通常是在其页面中加入page指令的errorPage属性来将其指向一个专门处理异常错误的页面。如果这个错误处理页面已经封装了这个页面收到的错误信息,并且错误处理页面页面含有的isErrorpage属性设置为true,则这个错误处理页面可以使用以下方法来访问错误的信息:
config内置对象是ServletConfig类的一个实例。在Servlet初始化的时候,JSP引擎通过config向它传递信息。这种信息可以是属性名/值匹配的参数,也可以是通过ServletContext对象传递的服务器的有关信息。
pageContext对象是一个比较特殊的对象。它相当于页面中所有其他对象功能的最大集成者,即使用它可以访问到本页面中所有其他对象,例如前面已经描述的request、response以及application对象等。

66.说一下jsp的4种作用域?

1)page:只在当前页面有效。
2)request:当前页面和请求界面有效。
3)session:整个会话期间都是有效的。
4)application:全局有效,除非服务器停止。

session:保存数据在服务器,开启会话界面会有保存信息在服务器,数据安全,但有效时间一般,大约30分钟,一般用来保存用户登录信息。
cookie:保存数据在本地,可保存一些不重要的信息储存在本地,数据不安全,方便下次等路附带提示功能,有效期长。

:当客户端请求服务器的时候,servlet会创建一个session对象,然后把数据存在session中,这样每次跳其他界面的时候,进入拦截器,验证session,如果能够从里面拿到数据,表示是已登录,不需要注册,如果没有,进行拦截,跳转到登录页面。

:每次请求服务器,会创建一个Session回话,并把Session id放在Cookie中,当在次访问的时候,他会判断本地有没有保存session id,如果没有,就会重新创建一个新session,如果有,就用这个session,所以可知,禁止Cookic,下次访问需要重行创建session,所以答案是不能用。

1)、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。

2)、由上边原因,SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
3)、由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。

6)、SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。

7)、SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。

8)、Spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。

71.如何避免SQL注入?

1)严格限制Web应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害。

2)检查输入的数据是否具有所期望的数据格式,严格限制变量的类型,例如使用regexp包进行一些匹配处理,或者使用strconv包对字符串转化成其他基本类型的数据进行判断。

3)对进入数据库的特殊字符(’"\尖括号&*;等)进行转义处理,或编码转换

4)所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中,即不要直接拼接SQL语句。例如使用database/sql里面的查询函数Prepare和Query,或者Exec(query string, args …interface{})。

5)在应用发布之前建议使用专业的SQL注入检测工具进行检测,以及时修补被发现的SQL注入漏洞。网上有很多这方面的开源工具,例如sqlmap、SQLninja等。

6)避免网站打印出SQL错误信息,比如类型错误、字段不匹配等,把代码里的SQL语句暴露出来,以防止攻击者利用这些错误信息进行SQL注入。

72.什么是XSS攻击,如何避免?

XSS攻击又称CSS,全称Cross Site Script (跨站脚本攻击),其原理是攻击者向有XSS漏洞的网站中输入恶意的 HTML 代码,当用户浏览该网站时,这段 HTML 代码会自动执行,从而达到攻击的目的。XSS 攻击类似于 SQL 注入攻击,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的,而在xss攻击中,通过插入恶意脚本,实现对用户游览器的控制,获取用户的一些信息。 XSS是 Web 程序中常见的漏洞,XSS 属于被动式且用于客户端的攻击方式。
1.利用 php htmlentities()函数对传入参数的非法的 HTML 代码包括单双引号等进行转义。但是,中文情况下, htmlentities() 却会转化所有的 html 代码,连同里面的它无法识别的中文字符也给转化了。
2.利用 php htmlspecialchars()函数对传入参数的非法的 HTML 代码包括单双引号等进行转义,需要注意的是第二个参数默认是 ENT_COMPAT,函数默认只是转化双引号("),不对单引号(’)做转义。更多的时候要加上第二个参数,应该这样用 :

73.什么是CSRF攻击,如何避免?

1)Get 请求不对数据进行修改
2)不让第三方网站访问到用户 Cookie
3)阻止第三方网站请求接口
4)请求时附带验证信息,比如验证码或者 token
使用HTTPS(HTTPS 还是通过了 HTTP 来传输信息,但是信息通过 TLS 协议进行了加密。)替换HTTP,对传输的数据进行加密,这样,当请求的信息被抓包工具抓包后,也无法修改提交的数据。

@1、throw:throw是语句抛出一个异常,一般是在代码块的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常。
throws:throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常),当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理

1)throws出现在方法函数头;而throw出现在函数体。
2)throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3)两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
1)在写程序时,对可能会出现异常的部分通常要用try{…}catch{…}去捕捉它并对它进行处理;
2)用try{…}catch{…}捕捉了异常之后一定要对在catch{…}中对其进行处理,那怕是最简单的一句输出语句,或栈输入e.printStackTrace();
3)如果是捕捉IO输入输出流中的异常,一定要在try{…}catch{…}后加finally{…}把输入输出流关闭;
4)如果在函数体内用throw抛出了某种异常,最好要在函数名中加throws抛异常声明,然后交给调用它的上层函数进行处理。

final:Java中的关键子,可用来修饰类、方法、属性,用在类上表示该类不能被继承,用在方法上表示该方法不能被重写,用在属性上表示该属性是常量不能修改。
finalize:finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

78.常见的异常类有那些?

异常非常多,Throwable 是异常的根类。

运行时异常不需要代码显式捕获处理。
下图是常见异常类及其父子关系:

79.HTTP响应吗301和302代表的是什么?有什么区别?

官方的比较简洁的说明:

ps:这里也顺带记住了两个比较相近的英语单词(permanently、temporarily),嘻哈! 详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

当一个网站或者网页24—48小时内临时移动到一个新的位置,这时候就要进行302跳转,打个比方说,我有一套房子,但是最近走亲戚去亲戚家住了,过两天我还回来的。而使用301跳转的场景就是之前的网站因为某种原因需要移除掉,然后要到新的地址访问,是永久性的,就比如你的那套房子其实是租的,现在租期到了,你又在另一个地方找到了房子,之前租的房子不住了。
使用301跳转的场景:
1)域名到期不想续费(或者发现了更适合网站的域名),想换个域名。
2)在搜索引擎的搜索结果中出现了不带www的域名,而带www的域名却没有收录,这个时候可以用301重定向来告诉搜索引擎我们目标的域名是哪一个。
3)空间服务器不稳定,换空间的时候。

使用302跳转的场景:
–尽量使用301跳转!
为什么尽量要使用301跳转?——网址劫持!
这里摘录百度百科上的解释:
从网址A 做一个302 重定向到网址B 时,主机服务器的隐含意思是网址A 随时有可能改主意,重新显示本身的内容或转向其他的地方。大部分的搜索引擎在大部分情况下,当收到302 重定向时,一般只要去抓取目标网址就可以了,也就是说网址B。如果搜索引擎在遇到302 转向时,百分之百的都抓取目标网址B 的话,就不用担心网址URL 劫持了。问题就在于,有的时候搜索引擎,尤其是Google,并不能总是抓取目标网址。比如说,有的时候A 网址很短,但是它做了一个302 重定向到B 网址,而B 网址是一个很长的乱七八糟的URL 网址,甚至还有可能包含一些问号之类的参数。很自然的,A 网址更加用户友好,而B 网址既难看,又不用户友好。这时Google 很有可能会仍然显示网址A。由于搜索引擎排名算法只是程序而不是人,在遇到302 重定向的时候,并不能像人一样的去准确判定哪一个网址更适当,这就造成了网址URL 劫持的可能性。也就是说,一个不道德的人在他自己的网址A 做一个302 重定向到你的网址B,出于某种原因, Google 搜索结果所显示的仍然是网址A,但是所用的网页内容却是你的网址B 上的内容,这种情况就叫做网址URL 劫持。你辛辛苦苦所写的内容就这样被别人偷走了。302 重定向所造成的网址URL 劫持现象,已经存在一段时间了。不过到目前为止,似乎也没有什么更好的解决方法。在正在进行的谷歌大爸爸数据中心转换中,302 重定向问题也是要被解决的目标之一。从一些搜索结果来看,网址劫持现象有所改善,但是并没有完全解决。

对于间接方式,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。它本质上是两次HTTP请求,对应两个request对象。

对于直接方式,客户端浏览器只发出一次请求,Servlet把请求转发给Servlet、HTML、JSP或其它信息资源,由第2个信息资源响应该请求,两个信息资源共享同一个request对象。

最后,祝大家都能找到一个称心满意的工作!

a) TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

b) TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。

c) UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

d) 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

e) TCP对系统资源要求较多,UDP对系统资源要求较少。

82.TCP为什么需要三次握手,两次不行吗?为什么?

以下部分转载自:tcp建立连接为什么需要三次握手

在《计算机网络》一书中其中有提到,三次握手的目的是“为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误”,这种情况是:一端(client)A发出去的第一个连接请求报文并没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间才到达另一端(server)B。本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立。(知乎上对上面的解释的评论:这个解答不是问题的本质,这个课本很多知识比较片面。问题的核心在于保证信道数据传输的可靠性,避免资源浪费仅仅是一个小的弱原因,不重要。)
问题的本质是,信道是不可靠的,但是我们要建立可靠的连接发送可靠的数据,也就是数据传输是需要可靠的。在这个时候三次握手是一个理论上的最小值,并不是说是tcp协议要求的,而是为了满足在不可靠的信道上传输可靠的数据所要求的。

83.说一下TCP粘包是怎么产生的?

这么说吧,TCP在传输数据的时候,是不区分边界的(数据和数据之间没有边界),因为是基于字节流,字节流是由字节组成,不包含边界数据的连续流。所以数据对TCP来说就是一大堆没有结构区别的字节块。那意味着什么?意味着TCP并不能对多个数据的整体的信息进行区分(打个比方:就像是你说一堆话没有标点符号全部连在一起,别人很可能弄错)或者对单个整体信息的错误区分(比如你要发送的整块数据被分成了好几块发送,然后这些数据在传输过程中可能由于网络原因,有的大数据块中被分片后的部分片段到了,可能由于接收端缓冲区满了,开始读取,而它们又没有边界之分,这时候就解释错误了)。那样就会使得我们要传输的信息可能粘连在一起,而被计算机解释错误。

而UDP是基于消息的传输服务,传输的是数据报,是有边界的。对于基于消息的协议来说,能保证对等方一次读操作返回的是一条消息,所以不会产生粘包问题。

84.OSI的七层模型都有那些?

物理层:物理接口规范,传输比特流,网卡是工作在物理层的。

数据层:成帧,保证帧的无误传输,MAC地址,形成EHTHERNET帧

网络层:路由选择,流量控制,IP地址,形成IP包

传输层:端口地址,如HTTP对应80端口。TCP和UDP工作于该层,还有就是差错校验和流量控制。

会话层:组织两个会话进程之间的通信,并管理数据的交换使用NETBIOS和WINSOCK协议。QQ等软件进行通讯因该是工作在会话层的。

表示层:使得不同操作系统之间通信成为可能。

应用层:对应于各个应用软件

2. GET的URL会有长度上的限制,则POST的数据则可以非常大。

3. POST比GET安全,因为数据在地址栏上不可见。
  (大佬说这写都是错的)

跨域是由浏览器的同源策略引起的,跨域访问,简单来说就是 A 网站的 js脚本代码试图去访问 B 网站(代码中包括提交内容和获取内容)。但是由于安全原因,跨域访问是被各大浏览器所默认禁止的,而跨域与否的判断是根据同源策略来的。

jsonp是解决跨域的一种方法
浏览器有一种约定叫同源策略,为了保证用户访问的安全,现代浏览器使用了同源策略,即不允许访问非同源的页面,但是有三个标签是允许跨域加载资源

 
 
 
 
 
 
}

        我们知道在JDK1.8中取消了永久代,区而代之使用了元空间来实现方法区。话虽如此,但是关于字符串常量池和运行时常量池的模棱两可的说法一直都是争论不休的。

1)方法区包含哪些内容?

2)运行时常量池存在什么地方?

3)取消永久代后,方法区的实现?

取消永久代后,使用元空间来实现方法区。
        在JDK1.8中,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。注意这里的剩余内容:说明原来移除从永久代移出的字符串常量池,静态常量,在更换了方法区实现后,并没有顺势进入到元空间,那么它们到哪里去了呢?

4)字符串常量池和运行时常量池究竟去了哪里?

father",变动的只是方法区中内容的物理存放位置。正如上面所说,类型信息(元数据信息)等其他信息被移动到了元空间中;但是运行时常量池和字符串常量池被移动到了堆中。但是不论它们物理上如何存放,逻辑上还是属于方法区的。

        JDK1.8中字符串常量池和运行时常量池逻辑上属于方法区,但是实际存放在堆内存中,因此既可以说两者存放在堆中,也可以说两则存在于方法区中,这就是造成误解的地方。

将字符串常量由永久代转移到堆中

        元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

5)关于为什么移除永久代?

某种程度上说,该图也是正确的:来源:

}

Java基础知识点总结

  • 一、Java基础(语言、集合框架、OOP、设计模式等)
  • 二、Java高级(JavaEE、框架、服务器、工具等)
  • 八、操作系统(OS基础、Linux等)

### 一、Java基础(语言、集合框架、OOP、设计模式等)

Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。

StrongReference:Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内,当没有任何对象指向它时将会被GC回收

SoftReference:尽可能长时间保留引用,直到JVM内存不足,适合某些缓存应用

WeakReference:顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, 下一次将被GC回收

PhantomReference:它是最弱的一种引用关系,也无法通过PhantomReference取得对象的实例。仅用来当该对象被回收时收到一个通知

WeakHashMap 的工作与正常的 HashMap 类似,但是使用弱引用作为 key,意思就是当 key 对象没有任何引用时,key/value 将会被回收。

当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops

理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G

代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。

当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一个后台进程,回收无效对象的内存用于将来的分配。

25. 怎么获取 Java 程序使用的内存?堆使用的百分比?

可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。

26. Java 中堆和栈有什么区别?

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。

可查看堆空间大小分配(年轻代、年老代、持久代分配)
提供即时的垃圾回收功能
垃圾监控(长时间监控回收情况)

查看堆内类、对象信息查看:数量、类型等

有了堆信息查看方面的功能,我们一般可以顺利解决以下问题:

年老代年轻代大小划分是否合理
垃圾回收算法设置是否合理

线程信息监控:系统线程数量。
线程状态监控:各个线程都处在什么样的状态下

Dump线程详细信息:查看线程内部运行情况

CPU热点:检查系统哪些方法占用的大量CPU时间
内存热点:检查哪些对象在系统中数量最大(一定时间内存活对象和销毁对象一起统计)

系统两个不同运行时刻,对象(或类、线程等)的不同

举例说,我要检查系统进行垃圾回收以后,是否还有该收回的对象被遗漏下来的了。那么,我可以在进行垃圾回收前后,分别进行一次堆情况的快照,然后对比两次快照的对象情况。

内存泄露的定义: 当某些对象不再被应用程序所使用,但是由于仍然被引用而导致垃圾收集器不能释放。

内存泄漏的原因:对象的生命周期不同。比如说对象A引用了对象B. A的生命周期比B的要长得多,当对象B在应用程序中不会再被使用以后, 对象 A 仍然持有着B的引用. (根据虚拟机规范)在这种情况下GC不能将B从内存中释放。这种情况很可能会引起内存问题,倘若A还持有着其他对象的引用,那么这些被引用的(无用)对象也不会被回收,并占用着内存空间。甚至有可能B也持有一大堆其他对象的引用。这些对象由于被B所引用,也不会被垃圾收集器所回收,所有这些无用的对象将消耗大量宝贵的内存空间。并可能导致内存泄漏。

1、当心集合类, 比如HashMap, ArrayList等,因为这是最容易发生内存泄露的地方.当集合对象被声明为static时,他们的生命周期一般和整个应用程序一样长。

b.清理程序中的重复的Jar文件,减少类的重复加载

发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap Space的size有关。解决这类问题有两种思路:

  1. 检查程序,看是否存在死循环或不必要地重复创建大量对象,定位原因,修改程序和算法。

  2. 通过虚拟机参数-Xms和-Xmx设置初始堆和最大堆的大小

直接内存并不是Java虚拟机规范定义的内存区域的一部分,但是这部分内存也被频繁使用,而且也可能导致OOM异常的出现。

JDK1.4引入了NIO,这是一种基于通道和缓冲区的非阻塞IO模式,它可以使用Native函数库分配直接堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,使得在某些场合显著提高性能,因为它避免了在Java堆和本地堆之间来回复制数据。

每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时 volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。

32. 双亲委派模型中的方法

一般来说 I/O 模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞 四种IO模型

在此种方式下,用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作以后,用户进程才能运行。 JAVA传统的 IO 模型属于此种方式!

在此种方式下,用户进程发起一个 IO 操作以后可返回做其它事情,但是用户进程需要时不时的询问 IO 操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的 CPU 资源浪费。其中目前 JAVA 的 NIO 就属于同步非阻塞 IO 。

此种方式下是指应用发起一个 IO 操作以后,不等待内核 IO 操作的完成,等内核完成 IO 操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问 IO 是否完成,那么为什么说是阻塞的呢?因为此时是通过 select 系统调用来完成的,而 select 函数本身的实现方式是阻塞的,而采用 select 函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!

在此种模式下,用户进程只需要发起一个 IO 操作然后立即返回,等 IO 操作真正的完成以后,应用程序会得到 IO 操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的 IO 读写操作,因为 真正的 IO读取或者写入操作已经由 内核完成了。目前 Java7的AIO正是此种类型。

BIO即同步阻塞IO,适用于连接数目较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4之前的唯一选择,但程序直观、简单、易理解。

NIO即同步非阻塞IO,适用于连接数目多且连接比较短的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO即异步非阻塞IO,适用于连接数目多且连接比较长的架构,如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK1.7开始支持

34. 类加载器按照层次,从顶层到底层,分别加载哪些类?

启动类加载器:负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。

扩展类加载器:这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

应用程序类加载器:这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器

在调用loadClass方法时,会先根据委派模型在父加载器中加载,如果加载失败,则会调用自己的findClass方法来完成加载


  • Statement是最基本的用法, 不传参, 采用字符串拼接,存在注入漏洞

由上可以看出,PreparedStatement有预编译的过程,已经绑定sql,之后无论执行多少遍,都不会再去进行编译,而 statement 不同,如果执行多遍,则相应的就要编译多少遍sql,所以从这点看,preStatement 的效率会比 Statement要高一些

  • 代码的可读性和可维护性

有很多的最佳实践,你可以根据你的喜好来例举。下面是一些更通用的原则:

a)使用批量的操作来插入和更新数据

d)通过列名来获取结果集,不要使用列的下标来获取

4. 数据库索引的实现

数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:

1、根结点至少有两个子女;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。

由于B-Tree的特性,在B-Tree中按key检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。

一个度为d的B-Tree,设其索引N个key,则其树高h的上限为logd((N+1)/2),检索一个key,其查找节点个数的渐进复杂度为O(logdN)。从这点可以看出,B-Tree是一个非常有效率的索引数据结构。

B-Tree有许多变种,其中最常见的是B+Tree,例如MySQL就普遍使用B+Tree实现其索引结构。

B+树是B树的变形,它把所有的data都放在叶子结点中,只将关键字和子女指针保存于内结点,内结点完全是索引的功能。

1、每个节点的指针上限为2d而不是2d+1。

2、内节点不存储data,只存储key;叶子节点存储data不存储指针。

一般在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增加了顺序访问指针。

在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针

例如图4中如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。

为什么B树(B+树)?

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。

这涉及到磁盘存取原理、局部性原理和磁盘预读。

先从B-Tree分析,根据B-Tree的定义,**可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。**为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:

每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。

综上所述,用B-Tree作为索引结构效率是非常高的。

而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

至于B+Tree为什么更适合外存索引,原因和内节点出度d有关。

由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。


1. 二叉搜索树:(Binary Search Tree又名:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。

二叉搜索树:(Binary Search Tree又名:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。

红黑树是一棵二叉搜索树,它在每个结点上增加一个存储位来表示结点的颜色,可以是RED或BLACK。通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树没有一条路径会比其他路径长出2倍,所以红黑树是近似平衡的,使得红黑树的查找、插入、删除等操作的时间复杂度最坏为O(log n),但需要注意到在红黑树上执行插入或删除后将不在满足红黑树性质,恢复红黑树的属性需要少量(O(log
n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为 O(log n) 次。具体如何保证?引出红黑树的5个性质。

红黑树的5个性质:满足以下五个性质的二叉搜索树

  1. 每个结点或是红色的或是黑色的
  2. 如果一个结点是红色的,则它的两个子结点是黑色的
  3. 对于每个结点,从该结点到其后代叶结点的简单路径上,均包含相同数目的黑色结点

由于性质的约束,插入的结点都是红色的。插入时性质1、3始终保持。破坏性质2当且仅当当前插入结点为根节点。变一下颜色即可。如果是破坏性质4或5,则需要旋转和变色来继续满足红黑树的性质。下面说一说插入的几种情况,约定当前插入结点为N,其父结点为P,叔叔为U,祖父为G

情形1:树空,直接插入违反性质1,将红色改黑。

情形2:N的父结点为黑,不必修改,直接插入

从情形3开始的情形假定N结点的父结点P为红色,所以存在G,并且G为黑色。且N存在一个叔叔结点U,尽管U可能为叶结点。

情形3:P为红,U为红(G结点一定存在且为黑)这里不论P是G的左孩子还是右孩子;不论N是P的左孩子还是右孩子。

首先把P、U改黑,G改红,并以G作为一个新插入的红结点重新进行各种情况的检查,若一路检索至根节点还未结束,则将根结点变黑。

情形4:P为红,U为黑或不存在(G结点一定存在且为黑),且P为G的左孩子,N为P的左孩子(或者P为G的右孩子,N为P的右孩子,保证同向的)。
P、G右旋并将P、G变相反色。因为P取代之前黑G的位置,所以P变黑可以理解,而G变红是为了不违反性质5。

情形5:P为红,U为黑或不存在,且P为G的左孩子,N为P的右孩子(或P为G的右孩子,N为P的左孩子,保证是反向的),对N,P进行一次左旋转换为情形4

删除操作比插入复杂一些,但最多不超过三次旋转可以让红黑树恢复平衡。

  • 黑高从某个结点x出发(不含x)到达一个叶结点的任意一条简单路径上的黑色结点个数称为该结点的黑高。红黑树的黑高为其根结点的黑高。
  • 一个具有n个内部结点的红黑树的高度h<=2lg(n+1)
  • 动态集合操作最坏时间复杂度为O(lgn)
  • 稳定排序:插入排序、冒泡排序、归并排序、基数排序

  • 适用于小数组,数组已排好序或接近于排好序速度将会非常快

  • 基数排序 分配+收集[稳定]

  • 不支持相同元素,没有稳定性问题

九大内部排序算法代码及性能分析参见我的

简单、均匀,不易产生冲突。但需事先知道关键字的分布情况,适合查找表较小且连续的情况,故现实中并不常用

平方取中法 折叠法 更多…

闭散列(开放地址方法):要求装填因子a较小,闭散列方法把所有记录直接存储在散列表中

  • 线性探测:易产生堆积现象(基地址不同堆积在一起)

开散列(链地址法):原地处理

  • 同义词记录存储在一个单链表中,散列表中子存储单链表的头指针。
  • 优点:无堆积 事先无需确定表长 删除结点易于实现 装载因子a>=1,缺点:需要额外空间

目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等。
想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树
出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树,
还要参考网上的代码,相当麻烦。
用跳表吧,跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它,
它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,

跳跃表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间),并且对并发算法友好。

Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,是一种“空间来换取时间”的一个算法,在每个节点中增加了指向下一层的指针,在插入、删除、查找时可以忽略一些不可能涉及到的结点,从而提高了效率。

在Java的API中已经有了实现:分别是

(1) 由很多层结构组成,level是通过一定的概率随机产生的

(2) 每一层都是一个有序的链表,默认是升序

(4) 如果一个元素出现在Level i 的链表中,则它在Level i 之下的链表也都会出现

(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素

在某一节点的左孩子的左子树上插入一个新的节点,使得该节点不再平衡。
举例 A B Ar Bl Br 在Bl下插入N,执行一次右旋即可,即把B变为父结点,原来的根节点A变为B的左孩子,B的右子树变为A的左子树。

与LL型是对称的,执行一次左旋即可。

指在AVL树某一结点左孩子的右子树上插入一个结点,使得该节点不在平衡。这时需要两次旋转,先左旋再右旋。

与LR对称,执行一次右旋,再执行一次左旋。

1、被删的节点是叶子节点

将该节点直接从树中删除,并利用递归的特点和高度的变化,反向推算其父节点和祖先节点是否失衡。

2、被删的节点只有左子树或只有右子树

将左子树(右子树)替代原有节点的位置,并利用递归的特点和高度的变化,反向推算父节点和祖先节点是否失衡。

3、被删的节点既有左子树又有右子树

找到被删节点的左子树的最右端的节点,将该结点的的值赋给待删除结点,再用该结点的左孩子替换它本来的位置,然后释放该结点,并利用递归特点,反向推断父节点和祖父节点是否失衡。

一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将对象存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,N是机器节点数。

1、考虑到比如一个服务器down掉,服务器结点N变为N-1,映射公式必须变为key%(N-1)

2、访问量加重,需要添加服务器结点,N变为N+1,映射公式变为hash(object)%(N+1)

当出现1,2的情况意味着我们的映射都将无效,对服务器来说将是一场灾难,尤其是对缓存服务器来说,因为缓存服务器映射的失效,洪水般的访问都将冲向后台服务器。

第二点:hash算法的单调性

Hash 算法的一个衡量指标是单调性,单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

consistent hash 也是一种hash 算法,简单的说,在移除 / 添加一个结点时,它能够尽可能小的改变已存在的映射关系,尽可能的满足单调性的要求。

第三点:将对象和服务器结点分别映射到环型空间

通常的一致性哈希做法是将 value 映射到一个 32 位的 key 值,也即是 0~2^32-1 次方的数值空间;我们可以将这个空间想象成一个首( 0 )尾( 2^32-1 )相接的圆环。

我们可以通过hash函数将我们的key映射到环型空间中,同时根据相同的哈希算法把服务器也映射到环型空间中,顺便提一下服务器或者某个计算节点的 hash 计算,一般的方法可以使用机器的 IP 地址或者机器名作为 hash 输入。

第四点:将对象映射到服务器

在这个环形空间中,如果沿着顺时针方向从对象的 key 值出发,直到遇见一个 服务器结点,那么就将该对象存储在这个服务器结点上,因为对象和服务器的hash 值是固定的,因此这个 cache 必然是唯一和确定的。

这时候考察某个服务器down机或者需要添加服务器结点,也就是移除和添加的操作,我们只需要几个对象的映射。

Hash 算法的另一个指标是平衡性 (Balance)。平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。

对于上述的做法,可能导致某些对象都映射到某个服务器,使得分布不平衡。为此可以采用“虚拟结点”的做法。

“虚拟结点”( virtual node )是实际节点在 hash 空间的复制品,一实际结点对应了若干个“虚拟节点”,这个对应个数也成为“复制个数”,“虚拟节点”在 hash 空间中以 hash 值排列。引入“虚拟结点”会让我们的映射分布更为平衡一些。

8. 如何判断链表是否有环

方法1:快慢指针法 2.设两个工作指针p、q,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样。比如p从A走到D,用了4步,而q则用了14步。因而步数不等,出现矛盾,存在环。

  • [排序算法] 快速排序

停止等待协议是最基本的数据链路层协议,它的工作原理是这样的。

在发送端,每发送完一帧就停止发送,等待接收端的确认,如果收到确认就发送下一帧。

在接收端,每收到一个无差错的帧,就把这个帧交付上层并向发送端发送确认。若该帧有差错,就丢弃,其他什么也不做。

停止等待协议为了可靠交付,需要对帧进行编号,由于每次只发送一帧,所以停止等待协议使用1个比特编号,编号0和1

停止等待协议会出现死锁现象(A等待B的确认),解决办法,启动超时计时器,超时计时器有一个重传时间。重传时间一般选择略大于“正常情况下从发完数据帧到收到确认帧所需的平均时间”。

再说滑动窗口之前,先说下连续ARQ,连续ARQ又称Go-back-N ARQ,意思是当出现差错必须重传时,要向回走N个帧,然后再开始重传,也就意味着只要有一帧出现差错,即使已经正确的帧也需要重传,白白浪费时间,增大开销。为此,应该对发送出去但未被确认的帧的数目加以限制,这就是滑动窗口协议。滑动窗口指收发两端分别维护一个发送窗口和接收窗口,发送窗口有一个窗口值Wt,窗口值Wt代表在没有收到对方确认的情况下最多可以发送的帧的数目。当发送的帧的序号被接收窗口正确收下后,接收端向前滑动并向发送端发去确认,发送端收到确认后,发送窗口向前滑动。收发两端按规律向前推进。

连续ARQ和选择重传ARQ均是窗口大于1的滑动窗口协议,而停止等待协议相当于收发两端窗口等于1。

滑动窗口指接收和发送两端的窗口按规律不断向前推进,是一种流量控制的策略。

1.HTTP/1.0协议使用非持久连接,即在非持久连接下,一个tcp连接只传输一个Web对象。
2.HTTP/1.1默认使用持久连接(然而,HTTP/1.1协议的客户机和服务器可以配置成使用非持久连接)。在持久连接下,不必为每个Web对象的传送建立一个新的连接,一个连接中可以传输多个对象。

1.安全性上说:get的方式是把数据在地址栏中明文的形式发送,URL中可见,POST方式对用户是透明的,安全性更高。
2.数据量说:Get传送的数据量较小,一般不能大于2KB,POST传送的数据量更大。
3.适用范围说:查询用Get,数据添加、修改和删除建议Post

5.TCP/IP体系各层功能及协议

5.1 网络接口层 -> 接收和发送数据报

主要负责将数据发送到网络传输介质上以及从网络上接收TCP/IP数据报,相当于OSI参考模型的物理层和数据链路层。在实际中,先后流行的以太网、令牌环网、ATM、帧中继等都可视为其底层协议。它将发送的信息组装成帧并通过物理层向选定网络发送,或者从网络上接收物理帧,将去除控制信息后的IP数据报交给网络层。

5.2 网际层 -> 数据报封装和路由寻址

网际层主要功能是寻址和对数据报的封装以及路由选择功能。这些功能大部分通过IP协议完成,并通过地址解析协议ARP、逆地址解析协议RARP、因特网控制报文协议ICMP、因特网组管理协议IGMP从旁协助。所以IP协议是网络层的核心。

网际协议IP:IP协议是一个无连接的协议,主要负责将数据报从源结点转发到目的结点。也就是说IP协议通过对数据报中源地址和目的地址进行分析,然后进行路由选择,最后再转发到目的地。需要注意的是:IP协议只负责对数据进行转发,并不对数据进行检查,也就是说,它不负责数据的可靠性,这样设计的主要目的是提高IP协议传送和转发数据的效率。

ARP:该协议负责将IP地址解析转换为计算机的物理地址。

虽然我们使用IP地址进行通信,但IP地址只是主机在抽象的网络层中的地址。最终要传到数据链路层封装成MAC帧才能发送到实际的网络。因此不管使用什么协议最终需要的还是硬件地址。

每个主机拥有一个ARP高速缓存(存放所在局域网内主机和路由器的IP地址到硬件地址的映射表)

(1)A在自己的ARP高速缓存中查到B的MAC地址,写入MAC帧发往此B

(2)没查到,A向本局域网广播ARP请求分组,内容包括自己的地址映射和B的IP地址

(3)B发送ARP响应分组,内容为自己的IP地址到物理地址的映射,同时将A的映射写入自己的ARP高速缓存(单播的方式)

注:ARP Cache映射项目具有一个生存时间。

RARP:将计算机物理地址转换为IP地址

ICMP:该协议主要负责发送和传递包含控制信息的数据报,这些控制信息包括了哪台计算机出现了什么错误,网络路由出现了什么错误等内容。

5.3 传输层 -> 应用进程间端到端的通信

传输层主要负责应用进程间“端到端”的通信,即从某个应用进程传输到另一个应用进程,它与OSI参考模型的传输层功能类似。

传输层在某个时刻可能要同时为多个不同的应用进程服务,因此传输层在每个分组中必须增加用于识别应用进程的标识,即端口。

TCP/IP体系的传输层主要包含两个主要协议,即传输控制协议TCP和用户数据报协议UDP。TCP协议是一种可靠的、面向连接的协议,保证收发两端有可靠的字节流传输,进行了流量控制,协调双方的发送和接收速度,达到正确传输的目的。

UDP是一种不可靠的、无连接的协议,其特点是协议简单、额外开销小、效率较高,不能保证可靠传输。

传输层提供应用进程间的逻辑通信。它使应用进程看见的就好像是在两个运输层实体间一条端到端的逻辑通信信道。

当运输层采用TCP时,尽管下面的网络是不可靠的,但这种逻辑通信信道相当于一条全双工的可靠信道。可以做到报文的无差错、按序、无丢失、无重复。

注:单单面向连接只是可靠的必要条件,不充分。还需要其他措施,如确认重传,按序接收,无丢失无重复。

1.发送之前无需建立连接,减小了开销和发送数据的时延

2.UDP不使用连接,不使用可靠交付,因此主机不需要维护复杂的参数表、连接状态表

3.UDP用户数据报只有8个字节的首部开销,而TCP要20字节。

4.由于没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(IP电话等实时应用要求源主机以恒定的速率发送数据是有利的)

UDP的过程(以TFTP举例):

1.服务器进程运行着,等待TFTP客户进程的服务请求。客户端TFTP进程启动时,向操作系统申请一个临时端口号,然后操作系统为该进程创建2个队列,
入队列和出队列。只要进程在执行,2个队列一直存在。

2.客户进程将报文发送到出队列中。UDP按报文在队列的先后顺序发送。在传送到IP层之前给报文加上UDP首部,其中目的端口后为69,然后发给IP层。
出队列若溢出,则操作系统通知应用层TFTP客户进程暂停发送。

3.客户端收到来自IP层的报文时,UDP检查报文中目的端口号是否正确,若正确,放入入队列队尾,客户进程按先后顺序一一取走。若不正确,UDP丢弃该报文,并请ICMP发送”端口不可达“差错报文给服务器端。入队列可能会溢出,若溢出,UDP丢弃该报文,不通知对方。

UDP首部:源端口 - 目的端口 - 长度 - 检验和,每个字段22字节。

注:IP数据报检验和只检验IP数据报的首部,而UDP的检验和将首部和数据部分一起都检验。

TCP报文段是面向字节的数据流。

TCP首部:20字节固定首部

确认比特ACK,ACK=1 确认号字段才有效;同步比特SYN:SYN=1 ACK=0表示一个连接请求报文段;终止比特FIN,FIN=1时要求释放连接。

窗口:将TCP收发两端记为A和B,A根据TCP缓存空间的大小确定自己的接收窗口大小。并在A发送给B的窗口字段写入该值。作为B的发送窗口的上限。意味着B在未收到A的确认情况下,最多发送的字节数。

选项:最大报文段长度MSS,MSS告诉对方TCP:我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节。若主机未填写,默认为536字节。

TCP的可靠是使用了序号和确认。当TCP发送一个报文时,在自己的重传队列中存放一个副本。若收到确认,删除副本。

TCP报文段的发送时机:1.维持一个变量等于MSS,发送缓存达到MSS就发送 2.发送端应用进程指明要发送,即TCP支持的PUSH操作。3.设定计时器

TCP的拥塞控制:TCP使用慢开始和拥塞避免算法进行拥塞控制

接收端根据自身资源情况控制发送端发送窗口的大小。

每个TCP连接需要维持一下2个状态变量:

接收端窗口rwnd(receiver window):接收端根据目前接收缓存大小设置的窗口值,是来自接收端的流量控制

拥塞窗口cwnd(congestion window):是发送端根据自己估计的网络拥塞程度设置的窗口值,是来自发送端的流量控制

慢开始算法原理:主机刚开始发送数据时,如果立即将较大的发送窗口的全部字节注入网络,由于不清楚网络状况,可能会引起拥塞。通常的做法是将cwnd设置为1个MSS,每收到一个确认,将cwnd+1,由小到大逐步增大cwnd,使分组注入网络的速率更加合理。为了防止拥塞窗口增长引起网络拥塞,还需设置一个状态变量ssthresh,即慢开始门限。

拥塞避免算法使发送端的拥塞窗口每经过一个RTT增加一个MSS(而不管在此期间收到多少ACK),这样,拥塞窗口cwnd按线性规律增长,拥塞窗口此时比慢开始增长速率缓慢很多。这一过程称为加法增大,目的在于使拥塞窗口缓慢增长,防止网络过早拥塞。

无论是慢开始还是拥塞避免,只要发送端发现网络出现拥塞(根据是没有按时收到ACK或者收到重复ACK),就将慢开始门限ssthresh设置为拥塞窗口值的一半并将拥塞窗口cwnd置为1,重新执行慢开始算法。这一过程称为乘法减小。目的在于迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。

上述TCP确认都是通过捎带确认执行的。

上述的慢开始和拥塞避免算法是早期TCP使用的拥塞控制算法。因为有时TCP连接会在重传时因等待重传计时器的超时时间而空闲。为此在快重传中规定:只要发送端一连收到三个重复的ACK,即可断定分组丢失,不必等待重传计数器,立即重传丢失的报文。

与快重传搭配使用的还有快恢复:当不使用快恢复时,发送端若发现网络拥塞就将拥塞窗口降为1,然后执行慢开始算法,这样的缺点是网络不能很快恢复到正常状态。快恢复是指当发送端收到3个重复的ACK时,执行乘法减小,ssthresh变为拥塞窗口值的一半。但是cwnd不是置为1,而是ssthresh+3xMSS。若收到的重复ACK
为n(n > 3),则cwnd=ssthresh+n*MSS.这样做的理由是基于发送端已经收到3个重复的ACK,它表明已经有3个分组离开了网络,它们不在消耗网络的资源。

注意的是:在使用快恢复算法时,慢开始算法只在TCP连接建立时使用。

每发送一个报文段,就对这个报文段设置一次计时器。新的重传时间=γ*旧的重传时间。

TCP连接建立和释放的过程

SYN置1和FIN的报文段要消耗一个序号。

应用层位于TCP/IP体系结构的最高一层,也是直接为应用进程服务的一层,即当不同的应用进程数据交换时,就去调用应用层的不同协议实体,让这些实体去调用传输层的TCP或者UDP来进行网络传输。具体的应用层协议有,SMTP 25、DNS 53、HTTP 80、FTP 20数据端口 21控制端口、TFTP 69、TELNET 23、SNMP 161等

按网络拓扑结构:总线、星型、环型、树型、网状结构和混合型。

按覆盖范围:局域网、城域网、广域网

按传播方式:广播网络和点对点网络

广播式网络是指网络中的计算机使用一个共享信道进行数据传播,网络中的所有结点都能收到某一结点发出的数据信息。

单播:一对一的发送形式。

组播:采用一对一组的发送形式,将数据发送给网络中的某一组主机。

广播:采用一对所有,将数据发送给网络所有目的结点。

点对点网络中两个结点间的通信方式是点对点的。如果两台计算机之间没有直连的线路,则需要中间结点的接收、存储、转发直至目的结点。

6. TCP的三次握手和四次挥手的过程

连接建立(三次握手):首先Client端发送连接请求报文SYN并进入SYN_SENT状态,Server收到后发送ACK+SYN报文,并为这次连接分配资源。Client端接收到Server端的SYN+ACK后发送三次握手的最后一个ACK,并分配资源,连接建立。

7. 为什么连接建立是三次握手,而连接释放要四次挥手?

因为当Server端收到Client端发送的SYN连接请求报文后,可以直接发送SYN+ACK报文,其中ACK用来应答,SYN用来同步。但是关闭连接时,当Server端收到FIN报文后,并不会立即关闭socket,所以先回复一个ACK,告诉Client端“你的FIN我收到了”,只有等Server端的所有报文发送完了,Server端才发送FIN报文,因此不能一起发送,故需要四次挥手。

8. 为什么TIME_WAIT状态需要2MSL(最大报文段生存时间)才能返回Closed状态?

这是因为虽然双方都同意关闭连接了,而且四次挥手的报文也都协调发送完毕。但是我们必须假想网络是不可靠的,无法保证最后发送的ACK报文一定被对方收到,因此处于LAST_ACK状态下的
Server端可能会因未收到ACK而重发FIN,所以TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

请求行由三部分组成,分别是请求方法,请求地址,Http版本

报文主体:只在POST方法请求中存在。

状态行:第一部分为Http版本,第二部分为响应状态码 第三部分为状态码的描述

返回内容:即Http请求的信息,可以是HTML也可以是图片等等。

Https即Secure Hypertext Transfer Protocol,即安全超文本传输协议,它是一个安全通信信道,基于Http开发,用于在客户机和服务器间交换信息。它使用安全套接字层SSL进行信息交换,是Http的安全版。

Https协议需要到CA申请证书,一般免费证书很少,需要交费。

Http是超文本传输协议,信息是明文传输,https则是具有安全性的tls/ssl加密传输协议。

11. 浏览器输入一个URL的过程

  1. 浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址

  2. 解析出IP地址后,根据IP地址和默认端口80和服务器建立TCP连接

  3. 浏览器发出Http请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器

  4. 服务器做出响应,把对应的请求资源发送给浏览器

中间人获取server发给client的公钥,自己伪造一对公私钥,然后伪造自己让client以为它是server,然后将伪造的公钥发给client,并拦截client发给server的密文,用伪造的私钥即可得到client发出去的内容,最后用真实的公钥对内容加密发给server。

解决办法:数字证书,证书链,可信任的中间人

误码率:传输错误的比特与传输总比特数的比率

计算冗余码(余数R)的方法:先补0(n个)再对生成多项式取模。

CRC只能表示以接近1的概率认为它没有差错。但不能做到可靠传输。可靠传输还需要确认和重传机制。

14. 数据链路层的协议

15. 截断二进制指数退避算法

是以太网用于解决当发生碰撞时就停止发送然后重发再碰撞这一问题。

截断二进制指数退避算法:基本退避时间为2τ k=min{重传次数,10} r=random(0~2^k-1) 重传所需时延为r倍的基本退避时间


八、操作系统(OS基础、Linux等)

“并行”是指无论从微观还是宏观,二者都是一起执行的,也就是同一时刻执行
而“并发”在微观上不是同时执行的。是在同一时间间隔交替轮流执行

2. 进程间通信的方式

  • 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

  • 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

  • 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  • 消息队列( message queue ) 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。

  • 套接字( socket ) :套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

以socket为例,在进程空间调用recvfrom,其系统调用知道数据包到达且被复制到应用进程的缓冲区或者发生错误才返回,在此期间一直等待,进程从调用recvfrom开始到它返回的整段时间内都是被阻塞的,因此称为阻塞IO

应用进程调用recvfrom,如果缓冲区没有数据直接返回EWOULDBLOCK错误。一般对非阻塞IO进行轮询,以确定是否有数据到来。

开启套接字接口信号驱动IO功能,并通过系统调用sigaction执行信号处理函数。当数据准备就绪时,为该进程生成SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主函数处理数据。

告知内核启动某个操作,并让内核在整个操作完成后通知我们。它与信号驱动IO的区别在于信号驱动IO由内核通知我们何时可以开始IO操作。而异步IO模型由内核通知我们IO操作已经完成。

1. 开源软件有哪些?

Apache:相对宽松与MIT类似的协议,考虑有专利的情况。适用:Apache服务器、SVN

GPL:GPLV2和GPLV3,如果你在乎作品的传播和别人的修改,希望别人也以相同的协议分享出来。

LGPL:主要用于一些代码库。衍生代码可以以此协议发布(言下之意你可以用其他协议),但与此协议相关的代码必需遵循此协议。

上面各协议只是针对软件或代码作品,如果你的作品不是代码,比如视频,音乐,图片,文章等,共享于公众之前,也最好声明一下协议以保证自己的权益不被侵犯,CC协议。

}

我要回帖

更多关于 java不是内部命令也不是可运行程序 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信