java运行时数据区内存

在这里插入图片描述

  1. 程序计数器
    每一个线程独享一个程序计数器,在运行时数据区唯一不会发生内存溢出的区域。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地 址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。

  2. 虚拟机栈
    每一个线程独占一个虚拟机栈。每当一个线程执行,jvm都会创建一个对应的栈帧。
    1)栈帧内部包含局部变量表、操作数栈、动态连接、方法出口等信息(因为虚拟机栈是线程独享的,对于局部变量表中的变量来说不会发生被多线程同时操作的数据安全问题)
    2)局部变量表的内存单位称为“槽”(slot),除了double,long基本数据类型占两个slot,其余数据类型和引用都占用一个slot。
    3)就HotSpot而言,如果线程请求的栈深度大于虚 拟机所允许的深度,将抛出StackOverflowError异常。可以通过调整 -Xss参数调整栈内存大小。
    4)操作数栈的深度在前端编译器编译成class文件时就已经确定,可以用javap -v命令查看每个方法的操作数栈的深度。

  3. 本地方法栈
    线程独享,用于执行本地方法使用的内存结构,HotSpot虚拟机没有单独对本地方法栈进行单独实现,与虚拟机公用一个内存区域。

  4. 堆内存
    所有线程共享。大部分对象都会被创建在堆中(如果栈上能分配下对象可以考虑直接分配到栈内存中,具体涉及栈上分配、标量替换等优化规则)gc重点光顾区域,对于传统的垃圾收集规则来说。会把堆内存划分为年轻代(Eden区,From Survivor, To Survivor),老年代。当通过FULLGC不能解决新的对象位置分配时,jvm会抛出OutOfMemoryError:head异常。堆内存最大最小可以使用-Xmx -Xms两个参数设置,为了防止堆内存增长时对资源的消耗,建议设置成一样。jdk7开始StringPool,静态变量从永久代改到堆中保存。
    年轻代内存可以通过,-Xmn(年轻代内存大小,数值)-XX:NewRatio=2(比例,old/new=2)数值配置(-Xmn)优先生效。
    1)Eden区,大多数对象会被创建在此区域,经过一次Minor gc后会将存活的对象复制到Survivor中去,然后整块内存回收。
    2)Survivor区,有两块相同大小的区域,每次Minor gc两块内存会交换From,To角色,当对象的年龄交换到一定年龄,此对象会晋升到老年代。可以通过参数-XX:SurvivorRatio=8(eden:from survivor: to survivor = 8:1:1),如果开启虚拟机自适应(-XX: +UserAdaptiveSizePolicy),这个默认值可能不太准确。
    3)老年代,超过Eden区一定比例的大对象会直接创建在老年代。经过一次Full GC后,对于不同的垃圾回收器会采用,标记清除,标记整理算法进行清理内存。
    4)堆内存被作为线程共享区域,不同线程同时为对象分配内存时,就会涉及到线程安全问题。这里解决线程安全主要有两种方式。
    CAS分配方案: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
    TLAB: 为每一个线程预先分配一块内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配。可以使用(-XX:+/-UseTLAB)jdk7及其以上默认开启。

  5. 方法区
    线程共享。类型信息、常量、即时编译器编译后的代码缓存等数据,对于方法区的内存回收会触发FULL GC,可以使用-Xnoclassgc参数关闭
    1)jdk7及其以前,方法区使用永久代方式实现使用(-XX: MaxPermSize, -XX: PermSize)参数可以控制。jdk6因为字符串常量,静态变量保存在此处,很容易造成此区域内存溢出OutOfMemoryError:PermSize
    2)jdk8改为元空间方式实现, 这里使用直接内存。

猜你喜欢

转载自blog.csdn.net/liha12138/article/details/106533061