码迷,mamicode.com
首页 > 其他好文 > 详细

HotSpot JVM基本原理(一)

时间:2014-08-19 11:09:45      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:jvm   垃圾回收   hotspot   新生代   编译器   java   

最近学习了JVM的相关知识,主要是关于HosSpot的,这里大致整理一下。


1.HotSpot JVM的结构

bubuko.com,布布扣

上图描述 HotSpot的大致结构,从图中我们可以看出JVM的大致流程是把一个class文件通过类加载器加载进系统,然后把放到不同的区域,通过编译器编译。


2.Heap结构

做过java的都知道,java的堆分为3个代,我们称之为分代管理。

bubuko.com,布布扣

上图可以看出三个代分别是年轻代,老年代和永久代。年轻代。这里有一种假设,假设对象初次建立的时候大部分都创建在年轻代。当对象年龄到达年轻代的上限,默认值是15,如果经过15次垃圾回收他还在新生代,那么他就会晋升成老年代。

那么什么是永久代呢?

永久代的意思就是这部分的内容默认情况下是不受垃圾回收的控制的,例如本地接口调用的数据存放在永久代,当那一个类被类加载器加载进来的时候,他里面的数据是存在永久代的,这些类的卸载以及本地接口的内存释放是非常严格的,针对这种情况,一般会放在永久代,垃圾回收器不做管理。当然也有例外。

由图还可以看出新生代分为3个区,Eden,S0,S1,S代表survival存活。这些会在后面提高。


3.内存回收和TLAB

bubuko.com,布布扣

首先上一张图,假设图中整个是一个内存区,例如新生代的Eden区,那么Eden是怎么分区的呢?先只看TLAB1,这代表一个线程在对象申请时状态的快照,也就是说一个线程初次申请的时候会在内存中划一块区域(深色代表申请了),它会有一个指针指向这个对象下一个连续空间的起始点(free_list),如果下一次要申请内存,通过指针向后挪下一次申请的尺寸量,这种机制就叫做Bump The Pointer。


其次因为一个对象要分配,那么一块区域就属于这个对象所拥有,其他对象是不能够进来的,如果把整个区域当成一个对象的话,那么申请这块内存的时候,就必须让内存块之前进行同步,这个同步的过程是通过锁来实现的,也就是说在申请这块内存的时候必须把这块内存加锁,等申请完了,下一个线程再来申请又要加锁。这里如果不对内存进行管理的话,那么就会导致内存在分配的时候效率是非常低的。


为了提高内存分配的效率,Oracle就提出一种机制,这种机制叫做TLAB,全称Thread Local Allocation Buffers。通过名称我们就可以看到,它对Eden这个区域大致划分成与线程数量相对应的区域,每个区域分配到一个线程里去,也就是说线程1只能在TLAB1分配内存,线程2只能在TLAB2内分配内存,依次类推。

那最后还有一块空间是怎么用的呢?

当某个线程在当前内存区域分配完了,它就必须在后面这个连续的区域在申请一块内存出来,也就是说这个线程就关联了两个TLAB,它就有很多Buffers。为了管理这些Buffers,这个线程的Free_list既要指向原来已经占满的内存,还要指向新的申请的内存的起始点,后面依次类推可能有N个。这样就摆脱了锁的机制,因为线程都是单线程操作,不存在并发问题,也就是在申请的时候仅仅在初始分配的区域申请即可,不用加锁,当申请满的时候在连续的区域申请时才会加锁,这就是所谓的并发同步。



4.垃圾回收算法

垃圾回收算法总体上讲有3种,第一种叫做复制算法,第二种叫做标记清除算法,第三种叫做标记整理算法,也叫标记清除压缩算法。


(1)复制算法

bubuko.com,布布扣

复制算法就是把存活的对象从一个空间复制到另一个空间,通过图可以看到这里上下分别是一块空间,他们是完全相同大小一样的两块内存区域。现在假设java虚拟机在运行的某一时刻是输入上面这种的,根对象(Roots)引用到第一块空间的A,A又引到了C,那么现在进行垃圾回收,因为两块内存完全相同大小一样,那么仅仅只需要将上面内存中根对象引用到的对象复制出来即可,复制后调整他们之间指针的引用。

但这有个缺点:这样一来上面这块内存区域和下面这块内存区域他们利用率只是50%。Oracle为了解决这个问题,他会把空间划分为3个块,也就是前面提到的Eden,S0,S1,他复制时并不是把一块内存等价复制到另一块内存中,他是把Eden和S0当中存活的对象复制到S1当中,如果S1装不下,那么年龄比较大的就会直接晋升到老年代。通过这种方式就大大提高了内存的利用率。这就是Oracle对复制算法的优化,而这种优化确实有效,因为大多数对象假设的存活时间不是很长,也就是一块内存回收过后,他的本身占用量是非常低的,这样就极大的提高了内存占用率。


(2)标记清除算法

bubuko.com,布布扣

标记清除算法和复制算法相比有类似点:他对复制算法的一个最大的好处就是提升了内存的利用率,但有一个缺点:产生了内存碎片。

初始状态依旧由根引用AA引用C。回收时,通过标记,标记了哪些对象是被引用了,也就是哪些对象不能够在这次垃圾回收中释放的对象,标记过后就会执行清除,清除就是把这块内存遍历一遍,但这个遍历也不是一个字节一个字节去遍历,会有算法会很快地位到哪些对象被标记哪些没被标记。图中这里他会把B对象清除。但清除有一个问题,B释放的空间以后可能还会用,所以会有一个指针指向这个地方,我们会发现清除过后区域和区域之间不连续,我们把B释放后的区域叫做内存碎片。


(3)标记清除压缩算法

bubuko.com,布布扣

标记清除压缩算法的前两步和标记清除算法是一样的。但会有一个整理的步骤,意思为B占在这个地方,回收的时候会把B后面的C向前挪,回收过后C的位置会发生变化。清除和压缩的动作就叫做整理。

也就是说标记压缩算法在标记清除算法之上,是对标记清除算法的一个优化。它不存在内存碎片,但它有个致命的缺点:在整理的过程中会产生一个对象拷贝的代价,这里就是把C拷贝到B中。而假若这里有ABCDAC被引用,BD要回收,因此会拷贝B和D,而且这个拷贝还不是一次性拷贝,那么这个拷贝代价非常高,因此这个算法用的时候要考虑慎重。


本文出自 “Xlows” 博客,请务必保留此出处http://xlows.blog.51cto.com/5380484/1541823

HotSpot JVM基本原理(一),布布扣,bubuko.com

HotSpot JVM基本原理(一)

标签:jvm   垃圾回收   hotspot   新生代   编译器   java   

原文地址:http://xlows.blog.51cto.com/5380484/1541823

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!