JVM内存分配(JDK8)

以一个Math类为列,从JVM运行时内存结构,到内存分配流程,最后内存分配参数的含义,串起整个流程。文章还待后续完善。

public class Math {
    public static final int initData = 666;

    public int compute(){
        int a = 1;
        int b = 2;
        int c = a+b * 10;
        return c;
    }

    public static void main(String[] args) {
        Math math = new Math();
        math.compute();
    }
}

运行时内存数据结构(JDK8)

JVM整体结构
JVM运行时数据结构包括:程序计数器、JVM栈、本地方法栈、堆、方法区。
1.程序计数器(Program Counter Register):当前线程执行字节码的行号指示器,保证多线程间切换时能恢复到正确的执行位置;
2.JVM栈(Java Virtual Machine Stack):Java方法执行的内存模型,每一个方法在执行的时候都会创建一个栈帧(Stack Frame)
栈帧里又包含局部变量表、操作数栈、动态链接和方法出口。
局部变量表(Local Variables Table):存放方法内的局部变量和方法参数。容量以变量槽(Variable Slot)为最小单位,存放8种类型:boolean、byte、char、short、int、float、reference或returnAddress类型。
操作数栈(Operand Stack):是一个操作过程中存放临时数据的中转区域,一个后入先出栈(LIFO)
动态链接(Dynamic Linking):对运行时常量池中方法的引用
方法出口(Return Address):
3.本地方法栈(Native Method Stack):使用本地Native方法服务分配的内存空间,比如:调用c++的动态库函数;
4.堆(Heap):几乎所有的类对象和数组都存放在堆上,细分为新生代和老年代;新生代又分为Eden区、From Survivor区和To Survivor区。
5.方法区(Method Area):存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
运行时常量池(Runtime Constant Pool):方法区一部分,用于存放编译期生成的Class文件中各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
6.直接内存(Direct Memory):通过NIO直接调用Native函数库分配堆外内存。

内存分配流程(JDK8)

内存分配流程

对象从创建开始,经历几个流程:
1.栈内分配:JVM通过逃逸分析确定该对象不会被外部访问,如果不会逃逸可以将该对象在栈内分配内存,这样该对象所占用的内存空间就可以随栈帧出栈而销毁,就减轻了垃圾回收的压力。
逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参 数传递到其他地方中。
标量替换:通过逃逸分析确定该对象不会被外部访问,并且对象可以被进一步分解时,JVM不会创建该对象,而是将该对象成员变量分解若干个被这个方法使用的成员变量所代替,这些代替的成员变量在栈帧或寄存器上分配空间,这样就不会因为没有一大块连续空间导致对象内存不够分配。
2.大对象直接进入老年代:大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下 有效。
3.TLAB(Thread Local Allocation Buffer):本地线程分配缓冲,把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存。
4.长期存活的对象将进入老年代:虚拟机给每个对象一个对象年龄(Age)计数器, 如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度 (默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中。
5.对象动态年龄判断:当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总大小大于这块Survivor区域内存大小的 50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了, 例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代。对象动态年龄判断机制一般是在minor gc之后触发的。
6.老年代空间分配担保机制:年轻代每次minor gc之前JVM都会计算下老年代剩余可用空间。如果用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象) ,就会看老年代的可用内存大小是否大于之前每一次minor gc后进入老年代的对象的平均大小。 如果结果是小于,那么就会触发一次Full gc,对老年代和年轻代一起回收一次垃圾, 如果回收完还是没有足够空间存放新的对象就会发生"OOM"异常。
7.Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快
8.Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢 10倍以上

内存分配参数

 java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eureka‐server.jar

‐Xms:初始堆内存大小
‐Xmx:最大堆内存
‐Xmn:年轻代大小
‐Xss:栈内存大小
‐XX:MetaspaceSize:设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小
‐XX:MaxMetaspaceSize:指定元空间触发Full gc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发 full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

paopaodog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值