JVM系列(3):堆(Heap)

简介: Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。

介绍


Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

 

堆区组成


  一.Young Generation Space :新生区 Young/New


  二.Tenure generation space :养老区 Old/ Tenure


  三.Permanent Space :永久区 Perm


  一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行


  堆在逻辑上可以分为三个部分:新生区,养老区,永久区(1.8后叫元空间)


  物理上分为:新生区,养老区


 

新生区 Young/New


新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分:


  伊甸区(Eden space):所有的类都是在伊甸区被 new 出来的。


  幸存者区(Survivor pace):幸存区有两个: 0 区(Survivor 0 space)和 1 区(Survivor 1 space),又叫from 和 to


  当伊甸园的空间用完时,程序又需要创建对象,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC,轻GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸存 0 区也满了,再对该区进行垃圾回收,然后移动到 1区。那如果 1 区也满了呢?再移动到养老区


 

养老区  Old/ Tenure


当对象在新生区经历过多次(默认 15 次)GC 依然幸存则进入养老区。若养老区也满了,那么这个时候将产生MajorGC(FullGC,重GC),进行养老区的内存清理。若养老区执行了 Full GC 之后发现依然无法进行对象的保存,就会产生OOM 异常“OutOfMemoryError”

如果出现 java.lang.OutOfMemoryError: Java heap space 异常,说明 Java 虚拟机的堆内存不够。


原因有二:


  (1)Java 虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx 来调整。


  (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。


 

永久区  Perm


 

永久存储区是一个常驻内存区域,用于存放 JDK 自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。如果出现 java.lang.OutOfMemoryError: PermGen space,说明是 Java 虚拟机对永久代 Perm 内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方 jar 包。


  例如:在一Tomcat 下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。


Jdk1.6 及之前: 有永久代, 常量池 1.6 在方法区


Jdk1.7:有永久代,但已经逐步“去永久代”,常量池 1.7 在堆


Jdk1.8 及之后: 无永久代,常量池 1.8 在元空间


  实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然 JVM 规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做 Non-Heap(非堆),目的就是要和堆分开。


  对于 HotSpot 虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口 interface)的一个实现,jdk1.7 的版本中,已经将原本放在永久代的字符串常量池移走。常量池(Constant Pool)是方法区的一部分,Class 文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量池中存放。

目录
相关文章
|
3月前
|
存储 算法 Java
[Java]散列表的数据结构以及对象在JVM堆中的存储过程
[Java]散列表的数据结构以及对象在JVM堆中的存储过程
54 1
[Java]散列表的数据结构以及对象在JVM堆中的存储过程
|
7月前
|
存储 Java 程序员
【面试题精讲】JVM-堆
【面试题精讲】JVM-堆
|
7月前
|
存储 Java
JVM之堆
JVM之堆
|
4月前
|
存储 Java 数据库
jvm性能调优 - 06线上应用部署JVM实战_堆内存预估与设置
jvm性能调优 - 06线上应用部署JVM实战_堆内存预估与设置
61 0
|
10天前
|
存储 安全 Java
JVM之本地方法栈和程序计数器和堆
JVM之本地方法栈和程序计数器和堆
11 0
|
2月前
|
存储 安全 Java
【JVM】Java堆 :深入理解内存中的对象世界
【JVM】Java堆 :深入理解内存中的对象世界
56 0
|
4月前
|
存储 Java
JVM之堆和方法区
JVM堆是Java程序运行时内存管理的核心,它主要用于存储对象实例和数组。堆内存的特点是动态分配和回收,它允许对象的创建和销毁,同时也需要注意内存泄漏和性能问题。
|
9月前
|
存储 算法 Java
【jvm系列-04】精通运行时数据区共享区域---堆
【jvm系列-04】精通运行时数据区共享区域---堆
87 1
|
9月前
|
Web App开发 消息中间件 Prometheus
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!(一)
Spring Boot 服务监控,健康检查,线程信息,JVM堆信息,指标收集,运行情况监控等!
|
5月前
|
存储 缓存 算法
JVM【带着问题去学习 01】什么是JVM+内存结构+堆内存+堆内存参数(逃逸分析)
JVM【带着问题去学习 01】什么是JVM+内存结构+堆内存+堆内存参数(逃逸分析)
34 0