1、JDK8后的虚拟机内存包含哪几个部分?做了哪些调整和改进?
JDK8之前的架构
堆
- 堆是JVM内存管理的最大一块区域,主要目的是存放对象的实例
- 所有新创建的对象实例和数组都会在堆上为其分配内存空间
- 线程共享
- 是垃圾收集器的主要管理区域,堆内存中可以存在物理上不连续的空间,只要逻辑上是连续的即可
- 如果在堆中没有内存完成实例分配,将会抛出OutOfMemoryError
栈
-
Java栈也称为虚拟机栈,是线程私有的
-
Java栈中存放的是一个个栈帧,没有栈帧对应一个被调用的方法,在栈帧中包括:
· 局部变量表
· 操作数栈
· 动态链接(调用另外一个方法)
和堆(heap)进行链接,堆(heap)中又常量池用于存放常量信息
· 方法返回地址和一些额外的附加信息
本地方法栈
-
专门为Native本地方法类实现的,线程私有
-
Java语言不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言(如C和C++)来实现对底层的访问
-
简而言之,一个本地方法就是一个Java调用非Java代码的接口。
程序计数器
- 它存储着当前线程所执行的字节码的行号
- 字节码解释器工作时,就是通过改变这个计数器的值,来让线程知道接下来需要执行哪条字节码指令
- 线程私有,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,每个线程之间 的计数器互不影响。
方法区(永久代)
- 方法区在JVM中也是一个非常重要的区域,它是被线程共享的区域
- 在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等
- 是一片连续的内存空间,同-XX:MaxPermSize可以设定永久代最大可分配空间
改进的原因:
- 为方法区分配多大的空间很难确定,因为它的大小依赖因素很多,比如JVM加载Class的总数,常量池的大小,方法的大小等
- 随着动态加载类的情况越来越多,这块内存变得越来越不可控
- 如果设置小了,当JVM加载的类信息容量超过了这个值,系统运行过程中就容易出现内存溢出错气,设置大了又浪费内存
JDK8架构
最大调整和改进
-
将方法区(永久代)进行了移除,新增了元空间的概念
-
元空间是放置在JVM内存空间之外的直接内存中,并且JDK8中对于方法区的修改参数PermSize和MaxPermSize已经失效
-
JDK8将类的相关信息放到元空间,将常量池和静态变量放到Java堆内
-
这种架构下就突破了设置大小的限制,限制可以使用更多的本地内存
直接内存
(1)这些内存直接受操作系统管理,而不是虚拟机
(2)这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响
(3)使用未公开的Unsafe和NIO包下ByteBuffer来进行分配