JVM: 对象的创建,类加载机制,垃圾回收机制,如何判断对象已死,垃圾回收的方法

对象的创建:

 

当虚拟机遇到一条new指令时,首先去常量池检查是否有这条指令所对应的符号引用,并且要检查这个符号引用所代表的类是否已经类加载了。如果没有,就必须先执行类加载的过程。然后就要为这个对象分配内存,相当于我们在java堆划分一块大小合适的内存给这个对象。但是划分内存的方式,要看java内存是否是规整,如果是,java堆就相当于,用过的内存在一边,没用的在另一边,然后是一个指针进行分割,给新对象分配时,就把指针向没有用过的那边偏移一点,偏移的距离是跟据对象所占内存大小来决定,这种方式是“指针碰撞”。如果不规整,java堆的空闲区域和已用是交叉的,那么需要一个列表去记录哪一块是已有哪一块空闲。然后再去选一块足够大的区域分配给这个对象实例,这种叫“空闲列表”。同时,还需要考虑对象创建时是很频繁的,在并发的情况下,线程并不安全。我可以对分配对象内存空间这种行为进行一个同步处理,还有就是每个线程可以在java堆细分一块属于自己的分配缓冲区,哪个线程要分配内存就在自己的分配缓存区去分配。内存分配完成后,需要将内存分配到的空间都初始化为零值,这样保证了我们在java代码中不用赋初值,就可以直接使用。然后虚拟机对对象进行一些必要的设置,如这个对象属于哪个类,如何找到这个对象的数据信息等信息,把这些信息存放在对象头。这样一个对象就算是创建出来了。

 

 

 

 

 

 

类加载机制:

虚拟机把class文件加载到内存中,进行安全校验,数据类型解析,初始化和内存分配。最终形成可以被虚拟机直接使用的java类型。

 

类加载 :加载--->验证---->准备---->解析---->初始化

1、加载:  加载阶段需要完成三件事:

    1)获取此类的二进制字节流

    2)将这个字节流所代表的静态储存结构转换成方法去运行时的数据结构

    3)在这个方法区中去生一个java.lang.class对象,作为方法区数据的访问入口

2、验证: 验证是时间最长的一个阶段,从加载阶段开始,持续到解析阶段,是进行一个class文件的二进制字节码的验证,看它是否满足虚拟机规范的二进制字节目,因为如果出现不安全的二进制字节码,可能会使虚拟机崩溃

  验证阶段又可以分为:文件格式验证、元数据验证、字节码验证、符号引用验证

  1.文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。

 

  2.元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。

 

  3.字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。

 

  4.符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。

 

3、准备:准备阶段是正式对对类变量进行分配和设置初始化值的一个阶段,这些变量都在方法区。,而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:

4、解析:解析阶段就是虚拟机将符号引用转化为直接引用的过程。主要包括四种类型:类或接口解析,字段解析,方法解析,接口方法解析

5、初始化:在准备阶段,类变量已经初始化过一次了,在这个阶段,则是根据程序员通过程序制定的计划区初始化类的变量和其它资源。

 

 

 

垃圾回收机制:

1、在jvm中,程序计数器,java栈,本地方法栈是不用进行垃圾回收的,因为他们的生命周期随线程同步,他们占用的内存会自动释放,只有方法区和java堆需要GC。

GC的主要任务是:

1、分配内存

2、确保被引用对象的内存不会被错误回收

3、回收不在被引用的对象的内存空间

垃圾回收主要解决的三个问题是:

1、那些内存需要回收

2、什么时候回收

3、如何回收

 

如何判断哪些内存应该被回收?(如何判断对象已死?)

判断方法:

1、引用计数算法:就是当一个地方引用它,计数器就+1,失效时,就-1,计数器=0,就代表它没有引用指向了。但是如果两个对象互相引用,就不会被回收了,因此引用算法也不可靠。

2、可达性算法:就是把一系列“GC roots" 作为起始点,然后向下搜索,路径称为引用链,当一个对象的GC root没有任何引用链相连,代表对象不可用。

 

什么时候被回收?

即使是不可达对象,也要进行二次筛选,如果对象没有覆盖finalize方法,或者finalize方法已经被虚拟机调用过了,就没有必要去执行。

如果有必要就在finalizer线程执行。

 

如何回收?垃圾收集算法

1、清除-标记法:

    第一次扫描对存活的对象进行标记,然后再进行第二次扫描,扫描整个空间中没有被标记的对象,然后回收。

   缺点:1、每个对象都有扫描,而且要扫描两遍,效率低,收集的时间也长

             2、产生不连续的内存碎片

 2、复制算法:

    把内存分为两块大小相等的区域,每次只使用其中一块,当这一快用完了, 就把存活的对象全复制到另外的一块内存区域中去,然后再清除前一块空间,这样就不会出现内存碎片问题。

   缺点:1、复制代价高  2、需要两个空间,总有一个空间是空闲的,浪费空间

3、标记-整理法:

   依然是把内存扫描一遍,标记出存活的对象,然后把存活的对象移动到内存的一边,然后清除边界以外的内存。

    这种方法也不会造成内存碎片,但是会有移动的成本

4、分代收集算法:

    这是不部分jvm的垃圾收集器采用的算法,它将对象存活的生命周期作为分类依据,分为新手代,老年代,永久代,老年代特点是每次收集时,只要少部分会被收集走,新生代就是大部分都会被回收,那么我们就可以根据不同代的特点去选择他们各自合适的收集算法。

 

JVM查看gc命令:

jstat -gc 12538 5000

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章