运行时数据区及 JVM 的整体内存结构

运行时数据区及 JVM 的整体内存结构 (理论篇)


本章知识点:
本地方法栈 方法区 元空间 运行时常量池 堆 直接内存(堆外内存)


-本地方法栈

本地方法栈跟 Java 虚拟机栈的功能类似,Java 虚拟机栈用于管理 Java 函数的调用,而本地方法栈则用于管理本地方法的调用。但本地方法并不是 用 Java 实现的,而是由 C 语言实现的(比如 Object.hashcode 方法)。
本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是 native 方法。你甚至可以认为虚拟机栈和本地方法栈是同一个区域。 虚拟机规范无强制规定,各版本虚拟机自由实现 ,HotSpot 直接把本地方法栈和虚拟机栈合二为一 。

-方法区

方法区(Method Area)是可供各条线程共享的运行时内存区域。它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool) 字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法
方法区是 JVM 对内存的“逻辑划分”,在 JDK1.7 及之前很多开发者都习惯将方法区称为“永久代”,是因为在 HotSpot 虚拟机中,设计人员使用了永久 代来实现了 JVM 规范的方法区。在 JDK1.8 及以后使用了元空间来实现方法区

-元空间

方法区与堆空间类似,也是一个共享内存区,所以方法区是线程共享的。假如两个线程都试图访问方法区中的同一个类信息,而这个类还没有装入 JVM, 那么此时就只允许一个线程去加载它,另一个线程必须等待。
在 HotSpot 虚拟机、Java7 版本中已经将永久代的静态变量和运行时常量池转移到了堆中,其余部分则存储在 JVM 的非堆内存中,而 Java8 版本 已经将方法区中实现的永久代去掉了,并用元空间(classmetadata)代替了之前的永久代,并且元空间的存储位置是本地内存。元空间大小参数:
jdk1.7 及以前(初始和最大值):-XX:PermSize;-XX:MaxPermSize;
jdk1.8 以后(初始和最大值) :-XX:MetaspaceSize; -XX:MaxMetaspaceSize
jdk1.8 以后大小就只受本机总内存的限制(如果不设置参数的话)
JVM 参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

Java8 为什么使用元空间替代永久代,这样做有什么好处呢?
官方给出的解释是:
移除永久代是为了融合 HotSpotJVM 与 JRockitVM 而做出的努力,因为 JRockit 没有永久代,所以不需要配置永久代。
永久代内存经常不够用或发生内存溢出,抛出异常 java.lang.OutOfMemoryError:PermGen。这是因为在 JDK1.7 版本中,指定的 PermGen 区大小为 8M,由于 PermGen 中类的元数据信息在每次 FullGC 的时候都可能被收集,回收率都偏低,成绩很难令人满意;还有为 PermGen 分配多大的空间很难 确定,PermSize 的大小依赖于很多因素,比如,JVM 加载的 class 总数、常量池的大小和方法的大小等。

-运行时常量池

运行时常量池(Runtime Constant Pool)是每一个类或接口的常量(Constant_Pool)的运行时表示形式,它包括了若干种不同的常量:从编 译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。
运行时常量池是方法区的一部分。运行时常量池相对于 Class 常量池的另外一个重要特征是具备动态性(Class 常量池在类加载章节会具体讲)。

-堆

堆是 JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在这里存储的。我们常说的垃圾回收,操作的对象就是堆。 堆空间一般是程序启动时,就申请了,但是并不一定会全部使用。堆一般设置成可伸缩的。 随着对象的频繁创建,堆空间占用的越来越多,就需要不定期的对不再使用的对象进行回收。这个在 Java 中,就叫作 GC(GarbageCollection)。 那一个对象创建的时候,到底是在堆上分配,还是在栈上分配呢?这和两个方面有关:对象的类型和在 Java 类中存在的位置。 Java 的对象可以分为基本数据类型和普通对象。

对于普通对象来说,JVM 会首先在堆上创建对象,然后在其他地方使用的其实是它的引用。比如,把这个引用保存在虚拟机栈的局部变量表中。 对于基本数据类型来说(byte、short、int、long、float、double、char),有两种情况。 当你在方法体内声明了基本数据类型的对象,它就会在栈上直接分配。其他情况,都是在堆上分配。
堆大小参数: -Xms:堆的最小值; -Xmx:堆的最大值; -Xmn:新生代的大小; -XX:NewSize;新生代最小值; -XX:MaxNewSize:新生代最大值; 例如-Xmx256m

-直接内存(堆外内存)

直接内存有一种更加科学的叫法,堆外内存。
JVM 在运行时,会从操作系统申请大块的堆内存,进行数据的存储;同时还有虚拟机栈、本地方法栈和程序计数器,这块称之为栈区。操作系统剩余的 内存也就是堆外内存
它不是虚拟机运行时数据区的一部分,也不是 java 虚拟机规范中定义的内存区域;如果使用了 NIO,这块区域会被频繁使用,在 java 堆内可以用 directByteBuffer 对象直接引用并操作;
这块内存不受 java 堆大小限制,但受本机总内存的限制,可以通过-XX:MaxDirectMemorySize 来设置(默认与堆内存最大值一样),所以也会出现 OOM 异

小结:
1、直接内存主要是通过 DirectByteBuffer 申请的内存,可以使用参数“MaxDirectMemorySize”来限制它的大小。
2. 其他堆外内存,主要是指使用了 Unsafe 或者其他 JNI 手段直接直接申请的内存。
堆外内存的泄漏是非常严重的,它的排查难度高、影响大,甚至会造成主机的死亡。后续章节会详细讲。
同时,要注意 Oracle 之前计划在 Java9 中去掉 sun.misc.UnsafeAPI。这里删除 sun.misc.Unsafe 的原因之一是使 Java 更加安全,并且有替代方案。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值