OOME:OutOfMemeryError
SOFE:StackOverflowError
程序计数器(Program Counter Register)是一块小的内存空间,是 当前线程所执行的字节码的行号指示器 。
在多线程的情况下,各个线程拥有独立计数器。
执行Java方法时,指向字节码指令地址,Native方法时为Undefined(此处没有规定OOME:JVM规范)。Java虚拟机栈(Java Virtual Machine Stacks),线程私有。
定义:方法执行的内存模型。
创建栈帧(Stack Frame),存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表:编译期可知的基本数据类型、对象引用和returnAddress(指向字节码指令地址)。
64位long和double占用2个局部变量空间(Slot),其他1个。
其空间在编译期完成分配。SOFE:线程请求的栈深度大于虚拟机允许的深度。
OOME:无法申请到额外的内存本地方法栈(Native Method Stack)。
为Native方法提供服务。
可能与JVMS合一(Sun Hotspot)。
同样也会抛出SOFE,OOME。所有线程共享。
作用:存放对象实例和数组,不是绝对(见11章11.3.5节后期优化相关)。
GC主要区域
细分:新生代,老年代。(Eden,From Survivor,To Survivor)
堆可以处于物理不连续的空间。(逻辑连续即可)
OOME: 内存不足且无法扩展时。NIO里使用Native函数库分配堆外内存。
通过堆中的DirectNyteBuffer对象作为引用进行操作。
好处:避免在Java堆和Native堆中来回复制数据。
可能出现OOME。
详细介绍见第3章方法区(Method Area)。
作用:存储加载的类信息、常量、静态变量、即时编译代码等。
是堆的一个逻辑部分,别名Non-Heap(非堆)。
别名:永久代,但本质上不等价。Hotspot基于永久代实现方法区,且GC可以扩展过去。
规范指明:可以不实现GC。
OOME:内存不足。方法区的一部分。
作用:存放字面量和符号引用。
运行期也可以放入,如String类的intern()方法。
会抛出OOME字面量:字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。比如字面量3,也就是指3。再比如 string类型的字面量"ABC", 这个"ABC" 通过字来描述, 所以就是字面量。
符号引用:
一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。
直接引用:
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
(3)一个能间接定位到目标的句柄见目录,HotSpot解释器代码片段
组成:对象头,实例数据,对齐填充。
包含两部分
MarkOop
偏向锁:
作用:在单线程(非并发情况,即没有竞争锁的时候)访问同步代码块的时候,可以忽略同步锁机制,来提升性能,
举例:
比如有一线程A,第一次访问同步代码块,申请锁对象,拿到锁对象后,把线程A的ID 写入对象头即偏向线程ID,会把锁对象的状态改为01,即偏向锁状态,此时线程A再次进入同步代码块的时候,则直接忽略掉同步代码块,这样就达到提高性能的作用。(在并发时,偏向锁是多余的们也可以理解为不存在的,因为他会自动升级为轻量级锁,和重量级锁,)各数据字段数据
HotSpot 要求起始地址为8字节的整数倍
实现方式:
工具:https://www.eclipse.org/mat/
Xms:堆最小值
Xmx:堆最大值
HeapDumpOnOutOfMemoryError:OOME时保存快照import java.util.ArrayList; import java.util.List; /** * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * @author ichukai */ public class HeapOOM { static class OOMObject{ } public static void main(String[] args){ List<OOMObject> list=new ArrayList<OOMObject>(); while (true){ list.add(new OOMObject()); } } } java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid83183.hprof ... Heap dump file created [27724413 bytes in 0.178 secs]
stack length:774 Exception in thread "main" java.lang.StackOverflowError
java.lang.OutOfMemoryError: unable to create new native thread
不要测试线程代码,会导致系统卡死
/** * VM Args: -Xss160k */ public class JavaVmStackSOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } private static void endlessLoop() throws Throwable { JavaVmStackSOF javaVmStackSOF = new JavaVmStackSOF(); try { javaVmStackSOF.stackLeak(); } catch (Throwable t) { System.out.println("stack length:" + javaVmStackSOF.stackLength); throw t; } } private static void leakByTread() { while (true){ Thread thread=new Thread(new Runnable() { public void run() { while (true){ } } }); thread.start(); } } public static void main(String[] args) throws Throwable { endlessLoop(); // leakByTread(); //不要测试线程代码,会导致系统卡死 } }
JDK7此方法已失效
import java.util.ArrayList; import java.util.List; /** * VM args: -XX:PermSize=10M -XX:MaxPermSize=10M */ public class RuntimeConstantPoolOOM { public static void main(String[] args){ // 保持常量池引用 List<String> list=new ArrayList<String>(); int i=0; while (true){ list.add(String.valueOf(i++).intern()); } } }
jdk7下前者为true,因为intern方法不再把值放到常量池,而是放实例引用,而后者“java”已经有引用了,所以不是同一个。
public class StringInternTest { public static void main(String[] args){ String str1=new StringBuilder("软件").append("开发").toString(); System.out.println(str1==str1.intern()); String str2=new StringBuilder("ja").append("va").toString(); System.out.println(str2==str2.intern()); } }
通过CGLib填充动态类信息,可导致OOM。
动态产生的大量JSP应用
基于OSGi应用,被不同加载器加载。由DirectMemory导致的内存溢出,在dump不会有明显异常,使用了NIO可以排查。
MaxDirectMemorySize 默认与Xmx(堆最大值)一样。
测试下来无效,后面再去看Java 规范import sun.misc.Unsafe; import java.lang.reflect.Field; /** * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M */ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); while (true) { unsafe.allocateMemory(_1MB); } } }