写点什么

了解 Java 内存模型

用户头像
陈皮
关注
发布于: 2020 年 06 月 21 日
了解 Java 内存模型



对于 Java 开发人员来说,了解 Java 内存模型是必不可少的,他们需要开发,部署,监视,测试和调整 Java 应用程序的性能。

Java Memory Model

运行资源密集型Java程序时,您必须使用以下某些JVM内存配置。

  • -XmsSetting - 初始化堆大小

  • -XmxSetting - 最大堆大小

  • -XX:NewSizeSetting - 新一代堆大小

  • -XX:MaxNewSizeSetting - 最大新生代堆大小

  • -XX:MaxPermGenSetting - 永久代的最大大小

  • -XX:SurvivorRatioSetting - 新的堆大小比率(例如,如果Young Gen大小为10m,并且内存开关为–XX:SurvivorRatio = 2,则将为Eden空间保留5m,为两个Survivor空间分别保留2.5m,默认值为8)

  • -XX:NewRatio - 提供新旧大小的比率(默认值= 2)

那么 JVM 如何驻留在内存中?就像任何其他软件一样,JVM占用主机OS内存上的可用空间。





但是,在 JVM 内部,存在单独的内存空间(堆,非堆,缓存),以便存储运行时数据和编译后的代码。





1 堆内4存

  • 堆分为两部分 - 年轻一代和老年一代

  • JVM 启动时分配堆(初始大小:-Xms)

  • 应用程序运行时堆大小增加/减少

  • 最大大小:-Xmx





1.1 新生代(Young Gen)

  • 保留用于包含新分配的对象

  • Young Gen 包括三个部分 - Eden Memory 和两个 Survivor Memory 空间(S0,S1)

  • 大多数新创建的对象进入 Eden 空间。

  • 当 Eden 空间中装满对象时,将执行次要 GC(又称 Young Collection),并将所有幸存者对象移至其中一个幸存者空间。

  • 次要 GC 还可以检查幸存者对象并将其移至其他幸存者空间。因此,一个幸存者空间始终是空的。

  • 在许多次 GC 循环后仍然存在的对象将移至老年代存储空间。通常,可以通过设置年轻一代对象的年龄阈值,然后才有资格晋升为老年代对象。

1.2 老年代(Old Generation)

  • 这是为包含可能在多轮次要 GC 中存活下来的长寿命对象而保留的

  • 当老年代空间已满时,将执行主要 GC(又称 Old Collection)(通常需要更长的时间)

2 非堆内存(Non-Heap Memory)

  • 包括永久代( Permanent Generation 从 Java 8 开始由元空间 - Metaspace 替换)

  • Perm Gen 存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码以及内部字符串

  • 可以使用 -XX:PermSize 和 -XX:MaxPermSize 更改其大小





3 缓存内存

  • 包括代码缓存.

  • 存储由 JIT 编译器生成的已编译代码(即原生代码 Machine Code),JVM 内部结构,已加载的探查器代理代码和数据等。

  • 当代码缓存超过阈值时,它将被刷新(并且 GC 不会重新定位对象)。

4 栈

这是一个很好的示例(来自 baeldung.com),说明了 Stack 和 Heap 如何有助于执行一个简单的程序(使用代码检查堆栈顺序)

class Person {
int pid;
String name;
// constructor, setters/getters
}
public class Driver {
public static void main(String[] args) {
int id = 23;
String pName = "Jon";
Person p = null;
p = new Person(id, pName);
}
}





注:仔细阅读供应商文档,以了解适用于您的 JVM 版本的工具。

5 常见内存相关问题

  • java.lang.StackOverFlowError - 栈内存已满

  • java.lang.OutOfMemoryError: Java heap space - 堆内存已满

  • java.lang.OutOfMemoryError: GC Overhead limit exceeded - GC已达到其开销限制

  • java.lang.OutOfMemoryError: Permgen space - 永久代空间已满

  • java.lang.OutOfMemoryError: Metaspace - 元空间已满(从 JDK8 开始)

  • java.lang.OutOfMemoryError: Unable to create new native thread - JVM 本机代码无法再从底层操作系统创建新的本机线程,因为已经创建了这么多线程,并且它们消耗了 JVM 的所有可用内存

  • java.lang.OutOfMemoryError: request size bytes for reason - 交换内存空间已被应用程序完全消耗

  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit - 我们的应用程序使用的数组大小大于基础平台允许的大小

但是,这些输出只能指示 JVM 的影响,而不能指示实际的错误。实际错误及其根本原因可能会出现在代码中的某处(例如内存泄漏,GC 问题,同步问题),资源分配甚至硬件设置。不建议您简单地增加受影响的资源来解决问题。需要监视资源使用情况,分析每个类别,进行堆转储,检查和调试、优化代码等。如果行不通再加资源。

References

Understanding JMM



发布于: 2020 年 06 月 21 日阅读数: 97
用户头像

陈皮

关注

还未添加个人签名 2018.04.26 加入

还未添加个人简介

评论

发布
暂无评论
了解 Java 内存模型