JVM内存详解

1,主流的四种JVM

oracle官网下载的jdk(商用版和社区版)基于hotspot实现,国内外bat等厂商基于openjdk做一些定制化jvm规范,有基于JRocket,J9。

hotspot:

Longview Technologies 公司在 1999 年首次发布了虚拟机 HotSpot--------->在 1997 年时, Sun 公司将 Longview Technologies 公司收购,从此之后 HotSpot 也就归属于 Sun 公司--------->Oracle 在 2009 年将 Sun 公司收购,目前为止 HotSpot 属于 Oracle,也是被使用最多、最广泛的虚拟机

JRockit:

Oracle 在 2008 年收购了 BEA 公司,JRockit 与 HotSpot 同属于 Oracle 目前为止 Oracle 一直在推进 HotSpot 与 JRockit 两款各有优势的虚拟机进行融合互补

J9: 

J9 VM 是 IBM 力推的一款虚拟机,主要应用在自己开发的的软件或服务器端,但可应用方向是多端多用途,如:嵌入式、服务端、桌面等,基本上IBM本司出品的产品都是用的J9 VM

 Harmony

IBM和Intel搞的开源JVM. IBM牵头,

2, JVM运行时数据区

(26 封私信 / 80 条消息) 一起来边打扫卫生边学习JVM运行时数据区 - 知乎 (zhihu.com)https://www.zhihu.com/zvideo/1396247897751535616

 私有:

             程序计数器(记录扫到第几块砖)

             本地方法栈(native  调用别的方法)

             虚拟机栈(栈帧、局部变量表、操作栈、动态链接、方法返回地址:

(26 封私信 / 80 条消息) JVM-用栈帧来炼制i++丹药 - 知乎 (zhihu.com)https://www.zhihu.com/zvideo/1398780456750505984

操作数栈 & 局部变量表解释:

i++药方子(先编译为.class文件,再用javap将其转换为阅读友好方式):首先,iload_1表示现在局部变量表1里面,将i放入操作数栈

 然后iinc1,1直接操作局部变量表,第一个位置加1

 对于++i:首先iinc1,1是对药方子

 1处的i进行加1操作,变为2,然后iloc_1,把药方子的i加到药鼎,及操作数栈。

由此可看出i++操作不具有原子性!!)

动态链接解释:

现有动态链接,再包装成栈帧,再调用方法。出栈时,考虑方法返回地址(正常退出或者异常返回)

比如A请求B:

一般不是interface、private,static、final等特殊字段时,翻译为字节码都是invokeVirtual,后面跟请求方法Methodxxxxxx(字符串变量),然后nvokeVirtual字节码执行方式会根据b的实际类型找类信息里是否有方法。但不会每次扫描找,有个vtable存b方法入口,找vtable即可。

公有:

元数据区: 1.7 在永久代内,1.8就移出heap外面了(因为比如类元信息,一直体重身高没怎么变,就干脆放外面了)     

String s=  new String(abc)这行代码,创建几个对象?

答:2个。abc是个常量,所以常量池有一个,s是个java对象,所以堆里有一个

堆:一个是非堆区(方法区),方法区也一般被称之为“永久代”。另外一个是堆区,分为young区和old区,young区又分为两个部分,一个是Eden区,一个是Survivor区(S0+S1),S0区也可以称之From区,S1也可以称之为To区。默认比例为8(Eden):1(一个survivor)

 一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法

复制算法的本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。


在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。

这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。        

为什么要有Survivor区?

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。你也许会问,执行时间长有什么坏处?频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。

好,那我们来想想在没有Survivor的情况下,有没有什么解决办法,可以避免上述情况:
在这里插入图片描述
显而易见,没有Survivor的话,上述两种解决方案都不能从根本上解决问题。

我们可以得到第一条结论:Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

 为什么要设置两个Survivor区?

设置两个Survivor区最大的好处就是解决了碎片化,下面我们来分析一下。

为什么一个Survivor区不行?第一部分中,我们知道了必须设置Survivor区。假设现在只有一个survivor区,我们来模拟一下流程:
刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。
我绘制了一幅图来表明这个过程。其中色块代表对象,白色框分别代表Eden区(大)和Survivor区(小)。Eden区理所当然大一些,否则新建对象很快就导致Eden区满,进而触发Minor GC,有悖于初衷。
在这里插入图片描述
碎片化带来的风险是极大的,严重影响Java程序的性能。堆空间被散布的对象占据不连续的内存,最直接的结果就是,堆中没有足够大的连续内存空间,接下去如果程序需要给一个内存需求很大的对象分配内存。。。画面太美不敢看。。。这就好比我们爬山的时候,背包里所有东西紧挨着放,最后就可能省出一块完整的空间放相机。如果每件行李之间隔一点空隙乱放,很可能最后就要一路把相机挂在脖子上了。

那么,顺理成章的,应该建立两块Survivor区,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代中。下图中每部分的意义和上一张图一样,就不加注释了。
在这里插入图片描述
上述机制最大的好处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片。

那么,Survivor为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果Survivor区再细分下去,每一块的空间就会比较小,很容易导致Survivor区满,因此,我认为两块Survivor区是经过权衡之后的最佳方案。

               

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洋气月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值