OOM内存JVM调优

1.Sun HotSpot VM,是JDK和Open JDK中自带的虚拟机,也是目前使用范围最广的Java虚拟机。

2.JVM内存分布

 

 

OOM内存JVM调优

 

 

程序计数器:是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。程序中的分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。由于多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,故该区域为线程私有的内存。

虚拟机栈:描述的是Java方法执行的内存模型,用于存储局部变量表、操作数栈、动态链接、方法出口等

堆:是Java虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,存放所实例,也是垃圾收集器管理的主要

方法区:用于存放已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。HotSVM针对该区域也进行GC,主要是常量回收以及类

 

3.JVM内存分配策略

对象的内存分配,在大方向上,是在Java堆上进行分配。

大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

大多数情况下,大对象直接进入老年代,虚拟机提供了参数来定义大对象的阀值,超过阀值的对象都会直接进入老年代。

经过多次Minor GC后仍然存活的对象(长期存活的对象),将进入老年代。虚拟机提供了参数,可以设置阀值。

4.JVM垃圾回收算法

标记-清除算法

:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当一块内存用完了,将还存另外一块上面,然后在把已使用过的内存空间一次清理掉。

标记-整理算法:标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所一端移动,然后直接清理掉端边界以外的内存。

分代收集算法:一般是把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。新生代都发现有大批对象死去,选用复制算法。老年代中因为对象存活率高,必须使用“标记-清理”或“标记-整理”算法来进行回收。

5.垃圾收集器

Serial收集器:是一个单线程的收集器,只会使用一个CPU或一条收集线程去完成垃圾收集工作,在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

ParNew收集器:是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为与Serial收集器完全一样。

CMS收集器:是一种以获取最短回收停顿时间为目标的收集器。过程分为以下四个步骤:

初始标记

并发标记

重新标记

并发清除

6.JVM常见启动参数

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小

-Xmn — 堆中年轻代的大小

-XX:-DisableExplicitGC — 让System.gc()不产生任何作用

-XX:+PrintGCDetails — 打印GC的细节

-XX:+PrintGCDateStamps — 打印GC操作的时间戳

-XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小

-XX:NewRatio — 可以设置老生代和新生代的比例

-XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布

-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值

-XX:TargetSurvivorRatio:设置幸存区的目标使用率

7.JAVA类生命周期

Java类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载七个阶段。

8.JVM类加载

启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 /lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现Java_Runtime_Home >/lib/extjava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。

系统(System)类加载器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加

双亲委派机制描述 :某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

9.JVM调优

查看堆空间大小分配(年轻代、年老代、持久代分配)

垃圾回收监控(长时间监控回收情况)

线程信息监控:系统线程数量

线程状态监控:各个线程都处在什么样的状态下

线程详细信息:查看线程内部运行情况,死锁检查

CPU热点:检查系统哪些方法占用了大量CPU时间

内存热点:检查哪些对象在系统中数量最大

 

一、OOM含义:

OOM,全称“Out Of Memory”,意思是“内存用完了”。 它来源于java.lang.OutOfMemoryError。   

二、为什么会出现java.lang.OutOfMemoryError:即OOM:

官方介绍为当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出 java.lang.OutOfMemoryError :··· (注意:这是个很严重的问题,因为这个问题已经严重到不足以被应用处理)。  

具体原因大致为两方面:

1、自身原因:比如虚拟机本身可使用的内存太少。

2、外在原因:如应用使用的太多,且用完没释放,浪费了内存。此时就会造成内存泄露或者内存溢出。

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。

内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。

三、OOM的error类型

首先说一下JAVA虚拟机运行时会管理的内存区域吧:

1. 程序计数器:当前线程执行的字节码的行号指示器,线程私有
2. JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
3. 本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
4. JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
5. 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
6. 运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
7. 直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。

所以除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

常见OOM情况:

1. java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。
2. java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。当出现此种情况时可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。注意,过多的常量尤其是字符串也会导致方法区溢出。
3. java.lang.*Error ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

四、OOM分析

Heap Dump(堆转储文件)它是一个Java进程在某个时间点上的内存快照。Heap Dump是有着多种类型的。不过总体上heap dump在触发快照的时候都保存了java对象和类的信息。通常在写heap dump文件前会触发一次FullGC,所以heap dump文件中保存的是FullGC后留下的对象信息。

通过设置如下的JVM参数,可以在发生OutOfMemoryError后获取到一份HPROF二进制Heap Dump文件: 

-XX:+HeapDumpOnOutOfMemoryError

生成的文件会直接写入到工作目录。  

注意:该方法需要JDK5以上版本。 

转存堆内存信息后,需要对文件进行分析,从而找到OOM的原因。可以使用以下方式:

mat: eclipse memory analyzer, 基于eclipse RCP的内存分析工具。具体使用参考:http://www.eclipse.org/mat/,推荐使用。   

jhat:JDK自带的java heap analyze tool,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言OQL,分析相关的应用后,可以通过http://localhost:7000来访问分析结果。不推荐使用。

 

 


 

上一篇:kubernetes 资源管理


下一篇:浅析 Spark Shuffle 内存使用