HotSpot虚拟机对象

来源:互联网 发布:java项目实战视频 编辑:程序博客网 时间:2024/05/17 22:05

HotSpot虚拟机对象 

HotSpot虚拟机在Java堆中对象分配、布局和访问的过程
1、对象创建
--①虚拟机遇到一个new指令时,首先将去检查这个指令的参数 是否能在 常量池(运行时常量池Runtime Constant Pool) 中定位到一个 类的  符号引用  ,并且检查这个符号引用代表的的类是否被加载过、解析和初始化过。   如果没有,必须先执行相应的类加载过程。
--②在类加载检查通过后,接下来虚拟机将为新生的对象分配内存。对象所需的大小在类加载完成后便可完成确定。为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。分配方式包括  指针碰撞 Bump the Pointer  和  空闲列表Free List。选择哪种方式由Java堆是否规整决定,而java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
----在使用Serial、ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞
----使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表
------JVM--标记-清除算法Mark-Sweep http://my.oschina.net/u/1377657/blog/388768

前言

垃圾自动回收机制的出现使编程更加的简单,使得我们不需要再去考虑内存分配和释放的问题,而是更加的专注在我们产品功能的实现上。但是我们还是需要花时间去了解下垃圾收集机制是怎么工作的,以便后面能够更好的进行我们应用的性能调优等。

目前最基本的垃圾收集算法有四种,标记-清除算法(mark-sweep),标记-压缩算法(mark-compact),复制算法(copying)以及引用计数算法(reference counting).而现代流行的垃圾收集算法一般是由这四种中的其中几种算法相互组合而成,比如说,对堆(heap)的一部分采用标记-清除算法,对堆(heap)的另外一部分则采用复制算法等等。今天我们主要来看下标记-清除算法的原理。

基本概念

在了解标记-清除算法前,我们先要了解几个基本概念。

首先是mutator和collector,这两个名词经常在垃圾收集算法中出现,collector指的就是垃圾收集器,而mutator是指除了垃圾收集器之外的部分,比如说我们应用程序本身。mutator的职责一般是NEW(分配内存),READ(从内存中读取内容),WRITE(将内容写入内存),而collector则就是回收不再使用的内存来供mutator进行NEW操作的使用。

第二个基本概念是关于mutator roots(mutator根对象),mutator根对象一般指的是分配在堆内存之外,可以直接被mutator直接访问到的对象,一般是指静态/全局变量以及Thread-Local变量(在Java中,存储在java.lang.ThreadLocal中的变量和分配在栈上的变量 - 方法内部的临时变量等都属于此类). 

第三个基本概念是关于可达对象的定义,从mutator根对象开始进行遍历,可以被访问到的对象都称为是可达对象。这些对象也是mutator(你的应用程序)正在使用的对象。

算法原理

顾名思义,标记-清除算法分为两个阶段,标记(mark)和清除(sweep). 

在标记阶段,collector从mutator根对象开始进行遍历,对从mutator根对象可以访问到的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象。

而在清除阶段,collector对堆内存(heap memory)从头到尾进行线性的遍历,如果发现某个对象没有标记为可达对象-通过读取对象的header信息,则就将其回收。

从上图我们可以看到,在Mark阶段,从根对象1可以访问到B对象,从B对象又可以访问到E对象,所以B,E对象都是可达的。同理,F,G,J,K也都是可达对象。到了Sweep阶段,所有非可达对象都会被collector回收。同时,Collector在进行标记和清除阶段时会将整个应用程序暂停(mutator),等待标记清除结束后才会恢复应用程序的运行,这也是Stop-The-World这个单词的来历。

接着我们先看下一般垃圾收集动作是怎么被触发的,下面是mutator进行NEW操作的伪代码:

New():    ref <- allocate()  //分配新的内存到ref指针    if ref == null       collect()  //内存不足,则触发垃圾收集       ref <- allocate()       if ref == null          throw "Out of Memory"   //垃圾收集后仍然内存不足,则抛出Out of Memory错误          return refatomic collect():    markFromRoots()    sweep(HeapStart,HeapEnd)

而下面是对应的mark算法:

markFromRoots():    worklist <- empty    for each fld in Roots  //遍历所有mutator根对象        ref <- *fld        if ref != null && isNotMarked(ref)  //如果它是可达的而且没有被标记的,直接标记该对象并将其加到worklist中           setMarked(ref)           add(worklist,ref)           mark()mark():    while not isEmpty(worklist)          ref <- remove(worklist)  //将worklist的最后一个元素弹出,赋值给ref          for each fld in Pointers(ref)  //遍历ref对象的所有指针域,如果其指针域(child)是可达的,直接标记其为可达对象并且将其加入worklist中          //通过这样的方式来实现深度遍历,直到将该对象下面所有可以访问到的对象都标记为可达对象。                child <- *fld                if child != null && isNotMarked(child)                   setMarked(child)                   add(worklist,child)

在mark阶段结束后,sweep算法就比较简单了,它就是从堆内存起始位置开始,线性遍历所有对象直到堆内存末尾,如果该对象是可达对象的(在mark阶段被标记过的),那就直接去除标记位(为下一次的mark做准备),如果该对象是不可达的,直接释放内存。

sweep(start,end):    scan <- start   while scan < end       if isMarked(scan)          setUnMarked(scan)      else          free(scan)      scan <- nextObject(scan)

缺点

标记-清除算法的比较大的缺点就是垃圾收集后有可能会造成大量的内存碎片,像上面的图片所示,垃圾收集后内存中存在三个内存碎片,假设一个方格代表1个单位的内存,如果有一个对象需要占用3个内存单位的话,那么就会导致Mutator一直处于暂停状态,而Collector一直在尝试进行垃圾收集,直到Out of Memory。


----指针碰撞  假设内存绝对规整的,所有用过的内存都放在一边,空闲的内存放在一边,中间放着一个指针作为分界点的指示器,那所分配的内存就仅仅把那个指针向空闲空间那边挪动一段与对象大小相等的距离。
----空闲列表  如果Java堆中的内存不是规整的,已使用的内存和空闲内存互相交错,那就没办法简单地进行指针碰撞,虚拟机就必须维护一个列表,记录上哪些内存时可用的,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
--③如果对象创建在虚拟机中是非常频繁的行为,即使是仅仅一个指针所指向的位置,在并发的情况下也并不是线程安全的,
----可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针分配内存的情况。
----解决方案  第一   对分配内存空间的动作进行同步处理  ----实际上虚拟机采用CAS配上失败重试的方式保证更新操作系统的原子性(乐观锁的一种实现方式——CAS http://codecloud.net/10459.html
----第二  把内存分配的动作按照线程划分在不同的空间之间进行,即   每个线程在Java堆中预先分配一小块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer  ,  TLAB),, 那个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。  虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB  参数设定。
--④内存分配完成后,虚拟机需要将分配的内存空间初始化为零值(不包括对象名),如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。  
----这一操作保证了对象的实例字段在Java代码中可以不赋初值就直接使用,程序能访问到这些字段的数据类型所对应的零值。






0 0
原创粉丝点击