java内存池是怎么划分的?

Posted

技术标签:

【中文标题】java内存池是怎么划分的?【英文标题】:How is the java memory pool divided? 【发布时间】:2010-11-18 17:44:41 【问题描述】:

我目前正在使用 jconsole 监控 Java 应用程序。内存选项卡可让您选择:

Heap Memory Usage
Non-Heap Memory Usage
Memory Pool “Eden Space”
Memory Pool “Survivor Space”
Memory Pool “Tenured Gen”
Memory Pool “Code Cache”
Memory Pool “Perm Gen”

它们之间有什么区别?

【问题讨论】:

假设您使用的是 Sun JDK,最好的答案将在他们的文档中找到:Tuning Garbage Collection (JDK 1.5) 和 Garbage Collection FAQ (JDK 1.4) 【参考方案1】:

堆内存

堆内存是 Java VM 为所有类实例和数组分配内存的运行时数据区域。堆的大小可以是固定的或可变的。垃圾收集器是一种自动内存管理系统,可为对象回收堆内存。

Eden Space:最初为大多数对象分配内存的池。

Survivor Space:包含在 Eden 空间的垃圾收集中幸存下来的对象的池。

Tenured GenerationOld Gen:包含在幸存者空间中已存在一段时间的对象的池。

非堆内存

非堆内存包括在所有线程之间共享的方法区域以及 Java VM 内部处理或优化所需的内存。它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码。方法区域在逻辑上是堆的一部分,但根据实现,Java VM 可能不会垃圾收集或压缩它。与堆内存一样,方法区的大小可以是固定的或可变的。方法区的内存不需要是连续的。

永久生成:包含虚拟机本身的所有反射数据的池,例如类和方法对象。对于使用类数据共享的 Java VM,这一代分为只读区和读写区。

代码缓存:HotSpot Java VM 还包括一个代码缓存,其中包含用于编译和存储本机代码的内存。

Here's some documentation on how to use Jconsole.

【讨论】:

我不确定@dfa 是否完全正确,因为 Java 虚拟机规范明确指出:“虽然方法区域在逻辑上是堆的一部分,但简单的实现可能会选择不进行垃圾收集或压缩它。”然而,很明显 jconsole 将代码缓存和永久生成显示为非堆,这似乎与规范相矛盾。谁能提供更多关于这个矛盾的澄清? @JamesBloom - 我也想知道。即使基本定义说明了哪个内存池属于哪个类型(堆/非堆),但它可以显式改变状态吗? 该文档被截取自:docs.intergral.com/pages/viewpage.action?pageId=22478944 该文档包含有关 JVM 的一些其他有用信息,值得浏览 尽管有很多赞成票,但实际上这并不是那么有意义的答案。例如,“在伊甸空间的垃圾收集中幸存下来的对象”是什么意思?这些物体是在幸存后从伊甸园转移到幸存者空间,还是它们在伊甸园中的空间被视为幸存者空间?那么在 Eden 空间以外的池中进行垃圾收集呢?它会发生吗?完全不清楚。 并且不要忘记堆栈(在非堆端):)【参考方案2】:

new 关键字在 Java 堆上分配内存。堆是主内存池,可供整个应用程序访问。如果没有足够的内存可供该对象分配,JVM 会尝试通过垃圾回收从堆中回收一些内存。如果仍然无法获得足够的内存,则抛出OutOfMemoryError,并退出JVM。

堆被分成几个不同的部分,称为世代。随着对象在更多的垃圾收集中存活,它们被提升到不同的代。老一代不经常收集垃圾。因为这些对象已经被证明具有更长的生命周期,所以它们不太可能被垃圾回收。

当对象第一次被构造时,它们被分配在 Eden Space 中。如果他们在垃圾收集中幸存下来,他们就会被提升到幸存者空间,如果他们在那里生活的时间足够长,他们就会被分配到 Tenured Generation。这一代的垃圾收集频率要低得多。

还有第四代,称为永久代,或 PermGen。驻留在此处的对象不适合进行垃圾回收,并且通常包含 JVM 运行所必需的不可变状态,例如类定义和字符串常量池。请注意,PermGen 空间计划从 Java 8 中删除,并将替换为名为 Metaspace 的新空间,该空间将保存在本机内存中。 参考:http://www.programcreek.com/2013/04/jvm-run-time-data-areas/

【讨论】:

该图看起来非常不言自明...这对任何 GC 算法都有效吗? G1 有不同的设置。 @Pythoner 我认为深紫色的标志应该是-XX:PermSize 而不是-XX:MaxPermSize,因为它已经在上面定义了。【参考方案3】:

在 Java8 中,非堆区域不再包含 PermGen 而是 Metaspace,这是 Java8 中的一个重大变化,应该消除 java 的内存不足错误,因为元空间大小可以根据 jvm 所需的空间来增加类数据。

【讨论】:

其实有元空间和类空间:docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/…【参考方案4】:

Java 堆内存是操作系统分配给 JVM 的一部分内存。

对象驻留在称为堆的区域中。堆是在 JVM 启动时创建的,并且在应用程序运行时可能会增加或减少大小。当堆满时,垃圾会被收集。

您可以在下面的 SE 问题中找到有关 Eden Space、Survivor Space、Tenured Space 和 Permanent Generation 的更多详细信息:

Young , Tenured and Perm generation

自 Java 8 发布以来,PermGen 已被 Metaspace 取代。

关于您的查询:

    Eden Space、Survivor Space、Tenured Space 是堆内存的一部分 元空间和代码缓存是非堆内存的一部分。

代码缓存: Java 虚拟机 (JVM) 生成本机代码并将其存储在称为代码缓存的内存区域中。 JVM 出于多种原因生成本机代码,包括动态生成的解释器循环、Java 本机接口 (JNI) 存根以及由即时 (JIT) 编译器编译为本机代码的 Java 方法。 JIT 是迄今为止最大的代码缓存用户。

【讨论】:

【参考方案5】:

Heap 分为年轻代和老年代:

年轻一代:这是一个物体短暂生活的地方,分为两个空间:

伊甸园空间:当使用新关键字创建对象时,在该空间上分配内存。 幸存者空间(S0 和 S1): 这是包含在从 Eden 进行小型 Java 垃圾收集后幸存的对象的池 空间。

Old Generation:这个池基本上包含tenured和virtual (reserved)空间,将保存那些在Young Generation垃圾回收后幸存下来的对象。

Tenured Space:这个内存池包含经过多次垃圾回收后存活的对象意味着存活的对象 从幸存者空间收集垃圾之后。

说明

假设我们的应用程序刚刚启动。

所以此时所有这三个空间都是空的(Eden、S0、S1)。

每当创建一个新对象时,它就会被放置在 Eden 空间中。

当 Eden 空间已满时,垃圾收集过程(次要 GC)将在 Eden 空间上进行,并且所有幸存的对象都将移至 S0。

然后我们的应用程序继续运行,在垃圾收集进程下一次运行时在 Eden 空间中创建新对象,它会查看 Eden 空间和 S0 中的所有内容,并且任何幸存的对象都被移入 S1。

PS:根据对象应该在Survivor空间存活多少时间的配置,对象也可能来回移动到S0和S1,然后达到阈值对象会被移动到老年代堆空间。

【讨论】:

以上是关于java内存池是怎么划分的?的主要内容,如果未能解决你的问题,请参考以下文章

面试官常问系列:Java虚拟机内存四大问题,都在这了!

4 个关于Java 虚拟机内存的问题?

Java虚拟机的内存结构

Java虚拟机的内存结构

JVM内存划分和JAVA虚拟机规范

Java字符串常量池是什么?为什么要有这种常量池?