JVM中的堆,一般分为三大部分:新生代、老年代、永久代:
一:新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。
新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。
Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
ServivorTo:保留了一次MinorGC过程中的幸存者。
ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。
MinorGC的过程:MinorGC采用复制算法。首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区);然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。
二:老年代:主要存放应用程序中生命周期长的内存对象。
老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。
MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
三:永久代
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制.
其实,移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。我们可以通过一段程序来比较 JDK 1.6 与 JDK 1.7及 JDK 1.8 的区别,以字符串常量为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.paddx.test.memory;
import java.util.ArrayList;
import java.util.List;
public class StringOomMock {
static String base = "string" ;
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for ( int i= 0 ;i< Integer.MAX_VALUE;i++){
String str = base + base;
base = str;
list.add(str.intern());
}
}
} |
这段程序以2的指数级不断的生成新的字符串,这样可以比较快速的消耗内存。我们通过 JDK 1.6、JDK 1.7 和 JDK 1.8 分别运行:
JDK 1.6 的运行结果:
JDK 1.7的运行结果:
JDK 1.8的运行结果:
从上述结果可以看出,JDK 1.6下,会出现“PermGen Space”的内存溢出,而在 JDK 1.7和 JDK 1.8 中,会出现堆内存溢出,并且 JDK 1.8中 PermSize 和 MaxPermGen 已经无效。因此,可以大致验证 JDK 1.7 和 1.8 将字符串常量由永久代转移到堆中,并且 JDK 1.8 中已经不存在永久代的结论。现在我们看看元空间到底是一个什么东西?
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
现在我们在 JDK 8下重新运行一下代码段 4,不过这次不再指定 PermSize 和 MaxPermSize。而是指定 MetaSpaceSize 和 MaxMetaSpaceSize的大小。输出结果如下:
从输出结果,我们可以看出,这次不再出现永久代溢出,而是出现了元空间的溢出。
采用元空间而不用永久代的几点原因:
1、为了解决永久代的OOM问题,元数据和class对象存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出(因为堆空间有限,此消彼长)。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。
相关推荐
老年代则存放生命周期较长的对象,而永久代主要存储JVM自身的元数据,如类和方法对象。 垃圾回收机制是为了有效地管理内存,防止内存泄漏和内存溢出。在新生代,垃圾回收通常采用复制算法,将Eden区存活的对象复制...
在Java 8及以上版本,永久代被元空间替代,用于存储类的元数据。 堆内存的大小可以通过JVM启动参数来设置,如`-Xms`指定初始堆大小,`-Xmx`设定最大堆大小。通常建议这两个参数设置为相同值以避免频繁调整。另外,`...
现代JVM通常采用分代收集策略,根据对象生命周期的不同,将内存分为新生代、老年代和永久代(或元空间)。 JVM性能优化是一个重要的话题,包括堆内存调整、栈内存优化、GC调优、JVM参数设置等。例如,通过-Xms和-...
方法区,也称为永久代或元空间,存储了类的元数据,如类名、方法信息等。Java 8后,元空间取代了永久代,以减少Full GC的发生。 堆内存是所有线程共享的一块区域,用于存储对象实例。新生代和老年代是堆内存的两个...
4. **运行时数据区域**:详细描述了各个运行时数据区域的用途和生命周期,如新生代、老年代、永久代(在Java 8后被元空间取代)等。 5. **字节码指令集**:JVM执行的字节码指令集,包括数据操作、控制流程、对象...
从Java 8开始,原本的永久代被元空间取代,用于存储类的元数据如类型信息、常量、编译后的代码等。元空间使用本地内存,理论上可以无限大,但实际会受到操作系统限制。 5. 程序计数器: 每个线程都有自己的程序...
8. **元空间(Metaspace)**:从JDK 8开始,JVM的永久代被替换为元空间,元空间存储的是类的元数据,它的大小可以通过`-XX:MetaspaceSize`和`-XX:MaxMetaspaceSize`来设置。 了解并掌握JVM的GC机制,有助于我们更好...
- **元空间/永久代**:存储类的元数据,如类信息、常量、方法数据等。 2. `jmap`工具介绍: `jmap`是JDK自带的命令行工具,主要用于获取堆内存信息。它需要JVM以服务器模式运行并开启`-XX:HeapDumpPath`选项来...
元空间是Java 8中替换永久代的部分,用于存储类元数据。 6. **-Xss**: 指定每个线程的栈大小。如果线程需要处理复杂的递归或者持有大量局部变量,可能需要增加这个值。 7. **-XX:+UseConcMarkSweepGC/-XX:+...
为了有效地使用这类工具,你需要了解一些基本的JVM内存管理概念,如新生代、老年代、永久代(对于较旧的JVM)或元空间(对于Java 8及以上版本),以及不同的垃圾收集器,如Serial、Parallel、CMS、G1、ZGC和...
5. **方法区(Method Area)/永久代(Permanent Generation)**:这个区域存储已加载的类信息、常量、静态变量、即时编译器编译后的代码等。在现代JVM中,这部分功能通常由元空间(Metaspace)取代,以减少对内存的...
4. **永久代与元空间**:在JDK 8之前,类的元数据存储在永久代;之后,元空间取代了永久代,存储在本地内存中。可以通过`-XX:MaxMetaspaceSize`来设定元空间的最大大小。 5. **JVM性能监控与诊断工具**:如...
JVM使用垃圾回收机制管理堆内存,分为年轻代和老年代,以优化垃圾收集效率。当对象不再被引用时,垃圾回收器会清理它们以释放空间。 3. **本地方法栈**:针对 native 方法,非Java代码执行时使用的栈。这些方法通常...
- **垃圾收集(Garbage Collection, GC)**:自动内存回收,包括新生代、老年代、永久代(或元空间)的划分,以及各种GC算法如标记-清除、复制、标记-整理和分代收集。 - **对象分配与存活判定**:如Eden区、...
3. 方法区(Method Area):也称为永久代或元空间,存储类信息、常量、静态变量、编译后的代码等数据。在Java 8之后,元空间取代了永久代,存储在本地内存中。 4. 程序计数器(PC Register):每个线程也有一个独立...
- **方法区**:存储类信息、常量、静态变量等,Java 8后被元空间(Metaspace)取代,以减少对永久代的内存压力。 - **栈内存**:每个线程都有独立的栈,用于存储局部变量、方法调用等,如果栈深度过大可能会导致...
GC的工作机制包括新生代、老年代、永久代(在较新版本的JVM中已被元空间取代)等区域的划分,以及各种GC算法如复制算法、标记-清除算法、标记-整理算法和分代收集策略等。理解这些机制对于优化JVM性能至关重要。 ...
3. **方法区**:存储类信息、常量、静态变量等,也称为永久代,在现代JVM中通常被元空间(Metaspace)替代。 ### 垃圾收集(Garbage Collection) JVM自动进行内存清理,防止内存泄漏。垃圾收集器主要有串行、并行...
2. **方法区(Method Area)**(也称为永久代或元空间) - **运行时常量池**:存储类文件中的常量、符号引用等。包括文本字符串、基本类型、final常量、类信息等。符号引用在运行时被解析为直接引用,实现动态链接...
5、方法区:也称为永久代,存储类的信息、常量、静态变量等,JDK 8之后被元空间(Metaspace)取代,元空间使用的是本地内存而不是JVM堆。 垃圾回收(GC)是Java程序的重要组成部分,它负责自动清理不再使用的对象,...