jvm优化——内存模型

这篇主要来介绍jvm中的内存模型

一、jvm 内存模型划分

根据JVM规范,JVM 内存共分为:方法区,虚拟机栈,本地方法栈,堆,程序计数器五个部分
在这里插入图片描述
方法区(线程共享)
被所有方法线程共享的一块内存区域。
用于存储已经被虚拟机加载的类信息,常量,静态变量等。
这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。

java 虚拟机栈(线程私有)
每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。
每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。
通常所说的栈,一般是指在虚拟机栈中的局部变量部分。
局部变量所需内存在编译期间完成分配,
如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。
如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。

本地方法栈(线程私有)
和虚拟机栈类似,主要为虚拟机使用到的Native方法服务。也会抛出StackOverflowError 和OutOfMemoryError。

Java堆(线程共享)
被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。
对可以按照可扩展来实现(通过-Xmx 和-Xms 来控制)
当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

程序计数器(线程私有):
是当前线程锁执行字节码的行号治时期,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法,则为空。

二、jdk1.7和jdk1.8堆内存模型的区别

2.1、jdk1.7的堆内存模型

下面就是jdk1.7的堆内存模型图:
jdk1.7的堆内存由三部分组成:Young 年轻区(代)、Tenured 年老区、Perm 永久区
在这里插入图片描述

2.1.1、Young 年轻区(代)

Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中,
Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制
对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间
中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动
到Tenured区间。

当我们去new一个对象的时候,首先会把对象放到Eden区。当Eden区变满之后会把一些存活的对象转移到Survivor区(一些垃圾对象会被回收),在Survivor区会经过几次垃圾回收之后依然还存活(意思就是该对象还是有用的对象)那么就会Survivor区中的对象继续向Tenured区转移,以保证Young区有空间可用

需要注意的是当我们new一个对象不是一个大对象的话就是进入Young年轻区,那么如果new的一个对象是一个大对象的话就不会进入Young年轻区会直接进入Tenured年老区

2.1.2、Tenured 年老区

Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young
复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了
application级别的缓存,缓存中的对象往往会被转移到这一区间。

这里通常保存的是一些声明周期比较长的对象,但并不是说只要把对象放到Tenured区中就一定能够永久的保存下来;在Tenured区中也可能会被回收

2.1.3、Perm 永久区

Perm代主要保存class,method,filed对象,这部份的空间一般不会溢出,除非一次性
加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到
java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因
就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造
成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以
解决问题。

比如说我们启动了一个Tomcat,Tomcat中部署了我们的java应用,那么Tomcat想要把我们的这个java应用跑起来的话,他一定会去加载应用中的class类、接口等一些代码结构相关的内容就会存储到Perm永久区

2.1.4、Virtual区:

最大内存和初始内存的差值,就是Virtual区。

2.2、jdk1.8内存模型

在这里插入图片描述
由上图可以看出,jdk1.8的内存模型是由2部分组成,年轻代 + 年老代。
年轻代:Eden + 2*Survivor
年老代:OldGen
在jdk1.8中变化最大的Perm区,用Metaspace(元数据空间)进行了替换。
需要特别说明的是:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存
空间中,这也是与1.7的永久代最大的区别所在。
年轻区和年老区的工作方式和jdk1.7相同

jdk1.8的堆内存模型就变成的下面这样:
在这里插入图片描述

jdk1.8的堆内存整体会分为两块:一个是堆内存,一个是非堆内存
堆内存分为两个区域:一个是年轻区,一个是年老区
年轻区中分为:Survivor区(两个相同的Survivor【s0,s1】),Eden区

注:jdk1.8新增的Metaspace(元数据空间)并不属于堆内存,而是属于非堆内存
Metaspace主要分为两块:CodeCache(存放类Class)、CCS(类的压缩指针)
CCS:如果我们设置压缩指针开启的话就会使用,否则不会被使用

2.3、为什么要废弃jdk1.7中的永久区

官网给出了解释:http://openjdk.java.net/jeps/122

This is part of the JRockit and Hotspot convergence effort. JRockit
customers do not need to configure the permanent generation (since JRockit
does not have a permanent generation) and are accustomed to not
configuring the permanent generation.
移除永久代是为融合HotSpot JVM与 JRockit VM融合而做出的努力,因为JRockit没有永久代,不需要配置永久代。
注:JRockit是另一种jvm;

现实使用中,由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen。基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间。

大概意思就是说,我们有时候堆永久区做调整的时候并不是特别的好调整,因为在调整的时候分配的内存多可能有些资源用不了就会产生浪费,如果分配的少的话可能不够用或内存泄漏就会出现上面的异常;所以废弃永久区改为元空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值