1.程序计数器
定义:程序计数器(Program Counter Register)是JVM中一块较小的内存空间。解释器在解释JVM指令为机器码以供CPU执行时,会去程序计数器当中找到jvm指令的执行地址。
作用:记住下一条jvm指令的执行地址
特点:线程私有的、不会存在内存溢出
2.虚拟机栈
什么是虚拟机栈:
- 每个线程运行时所需要的内存,称为虚拟机栈。
- 每个Java方法执行时,Java虚拟机都会创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
- 每个栈由多个栈帧组成,每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
3.本地方法栈
什么是本地方法栈:本地方法栈与虚拟机栈的作用相似,当Java虚拟机调用一些本地方法时,需要给本地方法提供的一些内存空间。本地方法是指不是由Java代码编写的一些方法,Java代码可以通过本地方法与操作系统进行交互。
4.堆
什么是堆:堆是Java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,Java世界里几乎所有的对象实例都在这里分配内存。
特点:
- 因为堆是线程共享的,所以堆中对象都需要考虑线程安全的问题
- 有垃圾回收机制
5.方法区
5.1 什么是方法区
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区是一种规范,在HotSpot虚拟机中,永久代和元空间是方法区的具体实现,在JDK7时,HotSpot已经将原本放在永久代中的字符串常量池(StringTable)、静态变量等移至Java堆中,到了JDK8,完全放弃了永久代的概念,通过在本地内存中实现的元空间来代替,并把JDK7中永久代还剩余的内容全部移到元空间。也就是说:
- 在JDK8以前方法区的实现是永久代,永久代存在于堆内存中
- 在JDK8及JDK8以后方法区的实现是元空间,元空间存在于本地内存中
5.2 方法区的组成
JDK1.6时的JVM内存结构
JDK1.8时的JVM内存结构
5.3 运行时常量池
1.什么是运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本信息、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
2.常量池和运行时常量池的区别
常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量 等信息
运行时常量池:常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量 池,并把里面的符号地址变为真实地址
5.4 StringTable
1.什么是StringTable
一般将StringTable称为串池,用来存储字符串对象,且不允许字符串对象重复,在JDK7及JDK7以前,串池存在于方法区中的运行时常量池里,在JDK8以后,串池存在于堆内存中。
2.StringTable 特性
常量池中的字符串仅是符号,第一次用到时才变为对象
利用串池的机制,来避免重复创建字符串对象
字符串变量拼接的原理是 StringBuilder (1.8)
字符串常量拼接的原理是编译期优化
可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
- JDK1.7及以后 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串 池中的对象返回
- JDK1.6及以前 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回
注意:StringTable受垃圾回收器的管理。也就是说,StringTable中那些长时间没有被引用的字符串常量,是会被垃圾回收器回收的。
6.直接内存
6.1 直接内存概述
1.什么是直接内存
直接内存并不是Java虚拟机内存区域中的一部分。但是这部分内存被频繁地使用,而且也可能导致OutDfMemoryError异常出现。
在 JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆的 DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
2.作用
常见于 NIO 操作时,用于数据缓冲区
3.优缺点
分配回收成本较高、但读写性能高
6.2 直接内存的分配和回收原理
- 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法。
- ByteBuffer 的实现类内部,使用了 Cleaner(虚引用)来监测 ByteBuffer 对象,一旦 ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调 用 freeMemory 来释放直接内存