Java7、8、9内存结构的区别

Java内存结构是每个java程序员必须掌握理解的,这是Java的核心基础,对我们编写代码特别是并发编程时有很大帮助。由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。

首先,我们回顾一下Java程序执行流程:

如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)。
 

 那么本篇文章主要是要分析Runtime Data Area(运行时数据区)的结构。

 1. 运行时数据区分为几个部分?

根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

名称

特征

作用

配置参数

异常

程序计数器

占用内存小,线程私有,

生命周期与线程相同

大致为字节码行号指示器

虚拟机栈

线程私有,生命周期与线程相同,使用连续的内存空间

Java 方法执行的内存模型,存储局部变量表、操作栈、动态链接、方法出口等信息

-Xss

StackOverflowError

OutOfMemoryError

java堆

线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址

保存对象实例,所有对象实例(包括数组)都要在堆上分配

-Xms

-Xsx

-Xmn

OutOfMemoryError

方法区

线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址

存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

-XX:PermSize:

16M

-XX:MaxPermSize

64M

OutOfMemoryError

运行时常量池

方法区的一部分,具有动态性

存放字面量及符号引用


1.1 方法区

方法区是java虚拟机规范去中定义的一种概念上的区域,具有什么功能,但并没有规定这个区域到底应该位于何处,因此对于实现者来说,如何来实际方法区是有着很大自由度的。

永生代是hotspot中的一个概念,其他jvm实现未必有,例如jrockit就没这东西。java8之前,hotspot使用在内存中划分出一块区域来存储类的元信息、类变量以及内部字符串(interned string)等内容,称之为永生代,把它作为方法区来使用。

[JEP122][2]提议取消永生代,方法区作为概念上的区域仍然存在。原先永生代中类的元信息会被放入本地内存(元数据区,metaspace),将类的静态变量和内部字符串放入到java堆中。

为了龙清楚方法区那么需要解释两个名词:永久代和元空间

PermGen(永久代)

绝大部分Java程序员应该都见过“java.lang.OutOfMemoryError: PremGen space”异常。这里的“PermGen space”其实指的就是方法区。不过方法区和“PermGen space”又有着本质的区别。前者是JVM的规范,而后者则是JVM规范的一种实现,并且只有HotSpot才有“PermGen space”,而对于其他类型的虚拟机,如JRockit(Oracle)、J9(IBM)并没有“PermGen space”。由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。并且JDK 1.8中参数PermSize和MaxPermSize已经失效。

元空间

其实,移除永久代的工作从JDK 1.7就开始了。JDK 1.7中,存储在永久代的部分数据就已经转移到Java Heap或者Native Heap。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了Java heap;类的静态变量(class statics)转移到了Java heap。

JDK1.8对JVM架构的改造将类元数据放到本地内存中,另外,将常量池和静态变量放到Java堆里。HotSpot VM将会为类的元数据明确分配和释放本地内存。在这种架构下,类元信息就突破了原来-XX:MaxPermSize的限制,现在可以使用更多的本地内存。这样就从一定程度上解决了原来在运行时生成大量类造成经常Full GC问题,如运行时使用反射、代理等。所以升级以后Java堆空间可能会增加。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间的最大区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数指定元空间的大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对改值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

除了上面的两个指定大小的选项外,还有两个与GC相关的属性:

-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集。

-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集。

 

所以对于方法区,Java8之后的变化:

移除了永久代(PermGen),替换为元空间(Metaspace);
永久代中的 class metadata 转移到了 native memory(本地内存,而不是虚拟机);
永久代中的 interned Strings 和 class static variables 转移到了 Java heap;
永久代参数 (PermSize MaxPermSize) -> 元空间参数(MetaspaceSize MaxMetaspaceSize)
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值