【深入理解JVM】内存结构

类加载子系统

类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间。除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字 符串字面量和数字常量(这部分常量信息是 Class 文件中常量池部分的内存映射)。 

执行引擎

负责执行虚拟机的字节码。热点扫描。


运行时数据区域包括程序计数器,java虚拟机栈,本地方法栈,方法区和堆。其中,java虚拟机栈,本地方法栈,程序计数器是每个线程私有的。

程序计数器

可以看作是当前线程执行到的字节码的行号指示器。对于Java的多线程,为了使程序每次切换后能够恢复到正确的执行位置,因此每一个线程必须要有自己独立的程序计数器。如果线程执行的是Java方法,记录的是正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。

Java虚拟机栈

线程私有。每起一个线程,就会新建一个他私有的虚拟机栈。线程每执行一个方法,都会在该线程的虚拟机栈中,创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息。方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。


局部变量表 存放了各种基本数据类型,对象引用和ReturnAdress(你需要返回的字节码指令的地址)。

两种异常情况:

  • 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
  • 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。

可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:

java -Xss512M HackTheJava复制代码

本地方法栈

跟虚拟机栈差不多,只不过它是为Native Method服务的。

一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。

Native Method详解:blog.csdn.net/wike163/art…

www.cnblogs.com/HDK2016/p/7…

存放对象实例。所有线程共享

OOM异常

方法区

用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在HotSpot中,永久代是方法区的实现。因为GC分代收集拓展到方法区。方法区主要是废弃类和常量的收集,对于方法区,也可以选择不进行垃圾回收。

一般来说,方法区不进行垃圾收集。

在jdk1.8之后,HotSpot中,删去了永久代,永久代的相关信息存放在了元空间。

OOM异常。

运行时常量池

运行时常量池是方法区的一部分。

Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。

运行时常量池具有动态性,除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

String.intern()

如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。

详解:tech.meituan.com/2014/03/06/…

直接内存

java 的 NIO 库允许 java 程序使用直接内存。直接内存是在 java 堆外的、直接向系统申 请的内存空间。通常访问直接内存的速度会优于 java 堆。因此出于性能的考虑,读写频繁 的场合可能会考虑使用直接内存。由于直接内存在 java 堆外,因此它的大小不会直接受限 于 Xmx 指定的最大堆大小,但是系统内存是有限的,java 堆和直接内存的总和依然受限于 操作系统能给出的最大内存。

访问效率高,不受堆大小的影响。



猜你喜欢

转载自juejin.im/post/5c9346886fb9a0710504a1e5
今日推荐