JVM 内存结构
1、JVM 内存结构图
了解JVM优化,先搞清楚JVM 的内存结构,以下为JVM 内存结构图。
2、堆内存(Heap)
2.1、堆内存结构
-
堆内存的新生代分为Eden(伊甸园)和存活区(survivor),Survivor区由 FromSpace 和 ToSpace 组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
-
JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身.
-
堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据 GC(垃圾回收)算法回收。
-
JDK8 版本废弃了永久代,替代的是元空间(MetaSpace)。元空间与持久代(Perm Gen)类似,都是方法区的实现。两者最大区别是:元空间并不在 JVM 中,而是使用本地内存。
元空间有注意有两个参数:
- MetaspaceSize :初始化元空间大小,控制发生GC阈值。
- MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存。
2.2、新生代 && 老年代 && 持久代
- 新生成的对象,首先放到新生代的伊甸园(Eden)区,当Eden区空间满了,触发 Minor GC(清理新生代),存活下来的对象移动到 Survivor0(即From Survivor)区。
- Survivor0 区满后触发执行 Minor GC(清理新生代),Survivor0 区存活对象移动到 Survivor1(即To Survivor) 区。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务
,所以无论什么时候,总是有一块Survivor区域是空闲着的
。如此就保证了一段时间内总有一个 幸存区(Survivor)为空。所以,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间
。- 经过多次 Minor GC(清理新生代)仍然存活的对象移动到老年代(Tenured)。
堆大小 = 新生代 + 老年代
。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2
( 该值可以通过参数 –XX:NewRatio 来指定 )- 默认的,
Eden : from Survivor : to Survivor = 8 :1 : 1
( 可以通过参数–XX:SurvivorRatio
来设定 ),即:Eden = 8/10 的新生代空间大小,from Survivor = to Survivor = 1/10 的新生代空间大小
。
2.3、扩展说明 GC
-
Java 中的堆Heap 是 GC (垃圾回收)的主要区域
。 -
GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。
-
Minor GC 是 发生在新生代中的垃圾收集动作,所采用的是复制算法
-
Full GC 是 发生在老年代 的垃圾收集动作,所采用的是标记-清除算法。Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。
2.4、JVM 参考选项
项目 | 说明 |
---|---|
-Xms | 初始堆大小,单位m、g 。如:-Xms256m |
-Xmx(MaxHeapSize) | 最大堆大小,一般不要大于物理内存的80% ,如:-Xmx512m |
-XX:MaxNewSize(-Xmn) | 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90% . |
-Xss | 表示堆栈内存大小 。JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。 |
-XX:NewRatio | 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3 . |
-XX:SurvivorRatio | 新生代中 Eden 与 Survivor 的比值,默认值为 8 。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10 |
-XX:PermSize | 永久代(方法区)的初始大小 。一般应用设置初始化200m,最大1024m就够了。 |
-XX:MaxPermSize | 永久代(方法区)的最大值 。 |
-XX:+PrintGCDetails | 打印 GC 信息 。 |
3、栈内存(Stack)
- 虚拟机栈 :
虚拟机栈也是线程私有的,生命周期与线程相同
。主要存储基本数据类型(boolean、byte、、char、short、int、float、long、double)、对象引用 (reference类型,它不等同于对象本身
。可能是一个对象起始地址的引用指针,也可能指向代表一个对象的句柄或者此对象的相关位置) 和 returnAddress 类型(指向了一条字节码指令的地址)。目前大多数虚拟机可动态扩展,如果扩展时无法申请到足够的内存即会抛出 OutOffMemoryError 异常。
- 本地方法栈 :
与虚拟机栈作用是相似的,他们之间的区别是,虚拟机栈用来执行java方法(也就是字节码),而本地方法栈则为虚拟使用到的 native (用来修饰可供其他语言调用的方法
,如操作操作系统底层服务的方法)服务,部分虚拟机(譬如 sun HotSpot) 直接将虚拟栈和本地方法栈合二为一,与java虚拟机栈相同,也会抛出OutOffMemoryError异常。
4、方法区内存
方法区又叫静态区,跟堆一样,被所有的线程共享。
方法区用于存储虚拟机加在的类信息、常量、static静态变量、即时编译器编译后的代码等数据
。虽然java虚拟机把他标注为堆的一部分,但他也有一个别名“非堆”,用于与java堆区分。
静态常量池:也被称为 class文件常量池
。 主要存放:
1、字面量
:例如文本字符串,final修饰的常量。
2、符号引用
:例如类和接口的全限定名,字段的名称和描述符,方法的名称和描述符等。
运行时常量池:当类加载到内存中后,JVM 就会将静态常量池中的内容存放到运行时的常量池中,运行时常量池里面存储的主要是编译期间生成的字面量、符合引用等等
。
字符串常量池:其可以理解成运行时常量池分出来的一部分,类加载到内存的时候,字符串会存到字符常量池里面。
.