jvm之内存分配与垃圾回收

来源:互联网 发布:mysmartcamera监控软件 编辑:程序博客网 时间:2024/05/22 17:02

jvm之内存分配与垃圾回收

java运行时内存区结构

线程共享内存区

java堆区

它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配

方法区(永久代)

方法区在物理上也属于java堆区的一部分。它用于存储每一个类的结构信息,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。当开发人员在程序中通过Class对象中的getName、isInstance等方法获取信息时,这些数据都来自方法区。

运行时常量池

存放的为类中固定的常量信息、方法和域的引用信息。属于方法区的一部分,内存大小超过方法区的所能提供的最大值会有异常。

线程私有内存区

pc寄存器

一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器, Java 的多线程机制离不开程序计数器,每个线程都有一个自己的PC,以便完成不同线程上下文环境的切换。

java栈

每一个 JVM 线程都有自己的 java 虚拟机栈,这个栈与线程同时创建,它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

本地方法栈

本地方法栈则为虚拟机使用到的本地方法(比如使用c/c++代码编写的方法)服务。


内存分配原理

当语法层面使用new关键字创建一个java对象时,jvm会检查这个符号引用对应的类是否已经成功经历加载、解析和初始化等步骤。当完成这些装载步骤后,就可以确定出创建对象实例时所需要的内存空间大小,接下来jvm将会对其进行内存分配以存储生成的对象实例。如图6-3所示。
堆区和方法区是线程共享区域,由于对象实例的创建是非常频繁的,一次在并发环境下从对区划分内存空间是非线程安全的。TLAB在java堆区是一块线程私有区,它包含在Eden中,可以避免一系列非线程安全问题还可以提高内存分配的吞吐量,所以我们称这种分配方式为快速内存分配策略。
一旦TLAB空间分配内存失败时,jvm就会尝试通过加锁机制确保数据操作的原子性,从而直接在Eden空间分配内存。如果当Eden空间中也无法分配内存时,jvm就会执行minor GC ,直至可以在Eden空间分配内存为止。
最后jvm会初始化实例数据,更新pc寄存器中指令地址,最后一个java对象才算真正创建成功。

逃逸分析和栈上分配

如果希望提高GC的回收效率,可以使用堆外存储技术。可以对未发生逃逸的对象(方法内的对象能在方法外被访问到称为逃逸)在栈上分配内存空间。

垃圾回收

垃圾标记算法:根搜索算法
垃圾回收算法:标记清除算法(会产生碎片,适合老年代),复制算法(适合新生代),标记压缩算法(适合老年代)

四种full  GC的场景:

1、System.gc();

2、旧生代空间不足
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space 。

3、Permanet Generation空间满
PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满。

4、统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。




0 0
原创粉丝点击