梳理JVM内存结构、GC、类加载、AOP编程及性能监控——4

36 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第40天,点击查看活动详情

对象创建及方法调用

数据分类:基本类型与引用类型

基本类型包括:byte,short,int,long,char,float,double,boolean,returnAddress 引用类型包括:类类型,接口类型和数组。

数据存储:使用堆存储对象信息

方法调用:使用栈来解决方法嵌套调用,而栈内部由一个个栈帧构成,调用一个方法时,在当前栈上压入一个栈帧,此栈帧包含局部变量表,操作栈等子项,每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。表面上代码在运行时,是通过程序计数器不断执行下一条指令;而实际指令运算等操作是通过控制操作栈的操作数入栈和出栈,将操作数在局部变量表和操作栈之间转移。

这里对内存的分配做个深入的扩展,解释下基础类型的自动装箱 boxing 以"Object obj = new Object();"为例,一个空Object对象占用8byte空间,而obj引用占用4byte,此条语句执行后共占用12byte;而Java中对象大小是8的整数倍,则Boolean b = new Boolean(true)至少需要20byte(16byte+4byte),而如果直接使用基本数据类型boolean b = true则仅仅需要1byte,在栈帧中存储;为优化此问题,JVM提出了基本类型的自动装载技术,来自动化进行基本类型与基本类型对象间的转换,来降低内存的使用量。

内存集中管理(模型及GC机制)

  • JVM内存模型图如下

内存结构主要有三大块:堆内存、方法区和栈。 1.堆内存是JVM中最大的一块由Young Generation(年轻代、新生代)和Old Generation(年老代)组成,而Young Generation内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配。 2.方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆)。 3.又分为java虚拟机栈(方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧)和本地方法栈(虚拟机的Native方法执行的内存区)主要用于方法的执行。 4.内存设置参数: -Xms设置堆的最小空间大小。 -Xmx设置堆的最大空间大小。 -XX:NewSize设置新生代最小空间大小。 -XX:MaxNewSize设置新生代最大空间大小。 -XX:PermSize设置永久代最小空间大小。 -XX:MaxPermSize设置永久代最大空间大小。 -Xss设置每个线程的堆栈大小。 -XX:SurvivorRatio=x #Eden区与Survivor区的大小比值,默认为8(Eden:From-Survivor=8:1) -XX:NewRatio=x #年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)默认值为2,即年轻代:年老代=1:2(这里与数学的比值有差异)

  • Minor GC

对象分配先从Eden空间与From Survivor空间获取内存,当两者中空间不足时,进行Minor GC,将Eden与From Survivor空间存活的对象,Copy到To Survivor空间,然后清空Eden与From Survivor空间,之后将To Survivor空间对象年龄加1,并将To Survivor空间设置为From Survivor空间,保证Minor GC时To Survivor空间始终为空;而当对象年龄为15后(默认是 15,可以通过参数-XX:MaxTenuringThreshold 来设定),将存活对象放入老年代。

例外情况: 1.对于一些较大的对象( 即需要分配一块较大的连续内存空间)则是直接进入到老年代。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。避免在新生代采用复制算法收集内存时,在Eden区及两个Survivor区之间发生大量的内存复制。 2.为了更好的适应不同的程序,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入Old Generation,无需等到MaxTenuringThreshold中要求的年龄。

这里的疑问,按照默认的8:1:1设置,一个To Survivor空间占10%空间,每次Minor GC能保证To Survivor空间够用吗?IBM的研究表明,98%的对象都是很快消亡的,大部分的对象在创建后很快就不再使用。这里可以根据GC detail来查看和分析比例设置是否合理。

  • Full GC

工作:同时回收年轻代、年老代,按照配置的不同算法进行回收。 时机:在Minor GC触发时,会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间,如果大于,改为直接进行一次Full GC;如果小于则查看HandlePromotionFailure设置(是否允许担保,使用Old Gerneration空间担保),如果允许,那仍然进行Minor GC,如果不允许,则也要改为进行一次Full GC。 取平均值进行比较其实仍然是一种动态概率的手段,也就是说如果某次Minor GC存活后的对象突增,大大高于平均值的话,依然会导致担保失败,这样就只好在失败后重新进行一次Full GC。