网站首页 > 技术文章 正文
本篇主要介绍一下,JVM 运行时数据区的内容。
概述
首先大概介绍一下下图所示的内容。JVM 运行时数据区主要分为了两大部分的内容:线程共有的方法区(Method Area)和堆(Heap)、线程私有的虚拟机栈(VM Stack),本地方法栈(Native Method Stack)和程序计数器(Program Counter Register)。在数据区下面的执行引擎中又包含了:即时编译器(JITCompiler)和垃圾收集器(GC)。GC主要用于回收线程共享的区域(方法区和堆),对于私有的内存区域则方法执行完毕后系统自动释放。(在实际的程序当中,线程私有的内存区域会有很多份)
对于整个运行时数据区而言,外部交互的模块有执行引擎、本地库接口和类加载器。
下面分别介绍一下,各个内存区域的详细信息。
- 程序计数器(Program Counter Register [私有]):是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。① (此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError的区域)
- JVM栈(Java Virtual Machine Stacks [私有]):每个方法在执行的时候都会同时创建一个栈帧。JVM栈中包含了:局部变量表、操作栈、动态链接、方法出口等信息。
- 本地方法栈(Native Method Stacks [私有]):其内存结构类似于JVM栈,不过使用到的是本地库接口。
- 堆(Heap [共享]):堆区域是JVM内存管理里面最大的一块,几乎所有通过new出来的对象都放在此区域。在GC的过程中,绝大部分的内存回收发生在此区域。
- 方法区(Method Area [共享]):方法区主要用于存储被JVM加载的类信息、常量、静态变量、即时编译后的代码等。
- 运行时常量池(Runtime Constant Pool [方法区组成部分]):用于存放编译器生成的各种字面量和符号引用。
Java 堆
Java 堆是应用程序最为关心的内存空间,几乎所有的对象实例都存放在堆中。并且Java中的 GC 都是JVM自动管理的,开发者不需要手动释放内存空间。
下面以 HotSpot 为例,介绍一下堆的内存结构。
从上面堆的结构图可以看出,HotSpot 的堆结构包含了两大部分区域:新生代、老年代。新生代又分为:Eden区、from区、和to区,其中新创建的对象会存放在Eden区。当一次GC之后未被收集之后,对象将进入from区。from区和to区是对等的(分代收集/复制算法),其中只有一块区域可以使用。通常情况下,当GC次数达到15次之后,对象还存活的话,下一次GC时对象将进入老年代。如果新创建的对象在新生代存放不了的话,那么对象刚创建就在老年代中。
下面通过一段代码,看一下堆、方法区和栈的指向关系。
public class SimpleHeap { private int id; public SimpleHeap(int id) { this.id = id; } public void show(){ System.out.println("My ID is " + id); } public static void main(String[] args) { SimpleHeap s1 = new SimpleHeap(1); SimpleHeap s2 = new SimpleHeap(2); s1.show(); s2.show(); } }
上述代码对应的内存引用关系如下所示:
Java 栈
Java栈是一块线程私有的内存空间。线程执行的基本行为是函数调用,每次函数调用的数据都是通过Java栈传递的。
Java栈可以类比数据结构中的栈结构。每一次函数调用都会有一个对应的栈帧压入Java栈,每个函数调用的返回,都会有一个Java栈帧的弹出。一个栈帧中,至少要包含局部变量表、操作数栈和栈数据区几个部分。如下图所示:
JVM 提供了参数 -Xss 来指定线程的最大栈空间,这个参数也决定了函数调用的最大深度。如果调用深度过大,则系统会抛出 StackOverflowError。
局部变量表
局部变量表是栈帧的重要组成部分之一。它用于保存函数的参数、局部变量。局部变量表中的变量只对当前函数调用有效。
操作数栈
操作数栈也是栈帧的重要组成之一。它主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。如下图所示:
帧数据区
帧数据区主要用于支持常量池解析、正常方法返回和异常处理等。在帧数据区中保存着访问常量池的指针,方便程序访问常量池;异常处理表也是其重要组成的一部分。
栈上分配
栈上分配是JVM提供的一项优化技术。基本思想是,对于那些线程私有的对象,可以将它们打散分配在栈上,而不是在堆上。这样以来,当方法执行结束后,栈上的对象可以自动被释放,不需要GC的介入。下面看一段代码:
/** * 非逃逸对象的栈上分配:大对象不适合栈上分配 * * VM Args: -server -Xmx10M -Xms10M -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations * * @author xuefeihu * */ public class OnStackTest { public static class User { public int id = 0; public String name = ""; } public static void alloc() { User u = new User(); u.id = 5; u.name = "geym"; } public static void main(String[] args) { long b = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { alloc(); } long e = System.currentTimeMillis(); System.out.println(e - b); } }
运行上述代码需要开启逃逸分析,运行结果如下(笔者1.8的JDK):
???????从上图可以看出,循环分配了近 1.5G 的内存空间,而 GC 只进行了1次,且只释放了1M多的空间。从而可以看出,对象基本上都是在栈上分配的。
方法区
方法区是线程共享的内存区域,它用于保存系统的类信息,比如类的字段、方法、常量池等。
在 JDK6 或 JDK7 中,方法区可以理解为永久区(Perm)。可以使用 -XX:PermSize 和 -XX:MaxPermSize 指定,默认情况下为 64M。
在 JDK8 中,永久代被移除。取而代之的是元数据区,其大小可以使用-XX:MaxMetaspaceSize 指定,它是一块堆外的直接内存。如果不指定大小,默认情况下它可以耗尽系统内存。
参考:《深入理解Java虚拟机》、《实战Java虚拟机》
猜你喜欢
- 2024-09-21 Java并发编程:LongAdder | LongAccumulator 对比测试
- 2024-09-21 「Java技巧」优雅的统计程序的执行时间,别再用System.cur
- 2024-09-21 Flink SQL 知其所以然(九)| SQL 的时间语义
- 2024-09-21 ArrayList插入1000w条数据之后,我怀疑了jvm...
- 2024-09-21 《Java实战之内存模型》详解篇(java内存模型happens before)
- 2024-09-21 比反射更快!使用ASM获取class信息(ClassReader)
- 2024-09-21 了解Java线程优先级,更要知道对应操作系统的优先级,不然会踩坑
- 2024-09-21 让大学生写的一个计算时间的方法,有人看得出来是在做什么吗?这
- 2024-09-21 Java基础——Java多线程(Lock接口详解)
- 2024-09-21 JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解
- 1514℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 569℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 510℃MySQL service启动脚本浅析(r12笔记第59天)
- 486℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 485℃启用MySQL查询缓存(mysql8.0查询缓存)
- 467℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 446℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 444℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)