Java虚拟机内存管理模型

 

Java虚拟机内存管理模型

 

1.JVM运行时数据区域

 

JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;

blob.png

1.1介绍

方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。

 

1.1.1 Java堆(Heap)

对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java堆是垃圾收集器管理的主要区域。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。

如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

1.1.2 方法区(Method Area)

方法区(Method Area)(持久代\永久代)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。

当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

1.1.3 程序计数器(Program Counter Register)

程序计数器(Program Counter Register)是一块较小的内存空间,线程私有,它的作用可以看做是当前线程所执行的字节码的行号指示器。当执行java方法时,存储的为当前class的行号,执行本地native方法时,为空。

此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

1.1.4 JVM栈(JVM Stacks)

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

局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

1.1.5 本地方法栈(Native Method Stacks)

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

1.2参数控制

blob.png

§ -Xms设置堆的最小空间大小。

§ -Xmx设置堆的最大空间大小。

§ -XX:NewSize设置新生代最小空间大小。

§ -XX:MaxNewSize设置新生代最大空间大小。

§ -XX:PermSize设置永久代最小空间大小。

§ -XX:MaxPermSize设置永久代最大空间大小。

§ -Xss设置每个线程的堆栈大小。

 

2. GC算法

分配内存的方式:

指针碰撞:前提是堆内存中的空闲空间十分的规整,使用与未使用的空间全部为连续,只需移动一下指针就可以了;

空闲列表:针对堆内存中的空间零散的存在,虚拟机维护着一个列表,记录着哪里被分配了,哪里还空闲;

 

2.1 复制

blob.png

 

1.从根集合(GC ROOT) 开始,找到存活对象复制到To区;

2.From,To交换身份;

 

Tracing GC的根本思路就是:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡;

GC ROOT:一般为

1.所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;

2.一些静态数据结构里指向GC堆里的对象的引用;

3.(看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)

2.2 标记-清除

blob.png

 

2.3 标记-压缩

blob.png

 

2.4 标记-清除-压缩

blob.png

 

3 HotSpot 内存管理与GC3.1 分代管理

blob.png

 

GC类型:

Minor GC 针对新生代GC;

Major GC 针对老年代GC;

Full Gc 针对新生代、老年代、永久代的GC;

 

新生代:由Eden 和2个相同大小Survivor(from/to,s0/s1)构成,一般在Eden分配对象;

GC频率高,采用效率较高的复制算法;

老年代:存放新生代中经历多次gc仍然存活的对象; Gc频率低,gc使用标记清除压缩各 种组合;

3.2 垃圾回收器

blob.png

 

注:serial:串行;parallel:并行;

CMS: Concurrent Mark-Sweep

 

Serial Copying \ Parallel Scavenge\ ParNew 均使用复制算法,在Eden空间不足时触发;

 

Serial Copying:

特点:串行

适用场景:单CPU,新生代小, 是32位windows上默认选择;

对象分配在Old区场景:1)对象超过eden大小;2)大对象(-XX:PretenureSizeThreshold)

晋升规则:经历多次minor gc的对象,To Surivor 放不下的对象;

 

Parallel Scavenge:

特点:并行(多线程)( -XX:ParallelGCThreads),可根据minorgc频率时间动态调整 Eden/s0/s1大小;(-XX:+UseAdaptiveSizePolicy)

适用场景:多CPU,对暂停时间要求短的应用

对象分配在Old区场景:eden分配失败(PretenureSizeThreshold参数无效)

晋升规则:经历多次minor gc的对象,To Surivor 放不下的对象;

 

ParNew:Serial Copying多线程版本;

 

Serial MSC:

特点:单线程 是32位windows上默认选择

算法:标记-清除-压缩;

Parallel Compacting

算法:标记-压缩

使用场景:注重吞吐量及CPU资源敏感

Serial MSC 多线程版本

 

CMS:

算法:标记-清除

特点:缩短GC暂停时间 ,并发(-XX:ParallelGCThreads)

使用场景:要求暂停时间短,响应时间快的应用

使用组合:

blob.png

 

blob.png

 

 

欢迎加个人公众号

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值