java虚拟机简单总结

1 .  java运行时数据区域:

程序计数器,java 虚拟机栈,java堆,方法区(运行时常量池 )

程序计数器:是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。

java虚拟机栈: 与程序计数器一样也是线程私有的,生命周期与线程一样。虚拟机栈描述的是java方法执行的内存模型, 每个方法被执行的时候都会同时创建一个栈桢用于存储:局部变量表,操作栈,动态链接,方法出口等信息。

局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,long,double,float),对象引用和returnAddress 类型(指向了一条字节码指令的地址)。

堆:是java 虚拟机所管理的内存中最大的一块。主要存放对象的实例(数组),在虚拟机启动时创建。

方法区:与java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量等数据。运行时常量池也属于方法区。

2 . 垃圾收集算法

2.1判断对象是否存活的算法

引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1,当引用失效时就减1;任何时候计数器都为0的对象就是不可能在被使用的。

其实现简单,判定效率高。java没有选用,主要原因是它很难解决对象之间的相互循环引用的问题。

根搜索算法:主流的商用程序语言中,都采用的算法判断对象是否存活。

其基本思路是通过一系列的名为"GC Roots"的对象作为起点,从这些结点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,则证明此对象是不可用的。

在java 中,可以作为 GC Roots的对象的包括以下几种 :

*虚拟机栈中引用的对象

*方法区中的类静态属性的对象

*方法区中的常量引用的对象

*本地方法栈中JNI的引用对象。

2.2 垃圾收集算法

2.2.1 标记-清理算法:算法分为"标记"和"清理"两阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,

*缺点有两个:一个是效率问题,标记和清理过程的效率都不高,另一个是空间问题,标记清理之后会产生大量不连续的内存碎片,空间碎片太多可能导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续的内存而不得不提前触发另一次垃圾收集操作。

2.2.2 复制算法:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

优点:实现简单,运行高效

代价:这种算法的代价是将内存缩小为原来的一半。

现代商用虚拟机都采用这种算法来回收新生代,IBM的专门研究表明,新生代的对象98%是朝生夕死的,所以可以将内存分为一块交大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,

当回收时,将Eden 和Survivor中还存活的对象一次性拷贝到另外一块Survivor上,最后清理掉Eden和刚用国的Survivor空间。

HotSpot 虚拟机默认Eden和Survivor的比例是8:1,也就是每次新生代中可用内存中间为整个新生代的90%(80%+10%),只有10%的空间是被"浪费"的。

当Survivor 空间不够时,需要依赖其他内存(这里指老年代)进行分配担保。

2.2.3 标记-整理算法

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以老年代一般不能直接选用这种算法。

有人就提出了"标记-整理"算法,其标记过程仍然与"标记-清理"算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,



 

 3 内存分配与回收策略

3.1 对象优先在Eden 分配

3.2大对象直接进入老年代

3.3长期存活的对象将进入老年代

4.虚拟机类加载机制

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:

加载,连接(验证,准备,解析),初始化,使用和卸载。



 类加载器

虚拟机设计团队把类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流"这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块被成为"类加载器"。

类加载器分为以下三种:

* 启动类加载器(Bootstrap ClassLoader):这个类加载器负责将放在<JAVA_HOME>\bin目录中的,或者被-Xbootclasspath 参数指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类即使放在lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被java 程序直接引用。

*扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc,Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的 ,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

*应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现,由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称为它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

双亲委派模型:


 

上图所展示的类加载器之间的这种层次关系,就称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动器加载器外,其余的类加载器都应当有自己的父类加载器。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有 当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。

猜你喜欢

转载自dbp5588.iteye.com/blog/2325981