01.JVM内存区域
0101.运行时数据区
- 程序计数器
- 存储:下一段要执行的代码的地址
- 共享:线程私有
- 特点:占用非常小的内存空间
- OOM:唯一不会出现OOM的区域
- 虚拟机栈:
- 存储:
- 方法执行的线程内存模型,入栈和出栈,对应的是方法的调用和退出;
- 局部变量表:存储基本类型和引用类型、返回地址。
- 共享:线程私有
- OOM:
- SOF:
- 在入栈深度超过额定值会触发
- 线程内内存用完,没有地方放新的栈帧
- OOM:
- 要申请新线程会触发
- 线程要扩内存,但是没有足够的内存会触发(HotSpot不支持线程扩内存)
- SOF:
- 存储:
- 本地方法栈:
- 同虚拟机栈:
- 不同点:只存储本地方法调用的相关信息。
- 堆:
- 存储:
- 几乎所有的对象实例和数组
- 字符串常量池、类文件常量池
- 静态变量
- 共享:线程共享
- OOM:堆内存用完了,并且堆内存无法扩展。
- 特点:堆分为年轻代和老年代;年轻代分为Eden区和S0、S1区;Eden区中又包含TLAB区;TLAB区是线程私有的。
- 存储:
- MetaSpace(元数据区):
- 存储:
- 运行时常量池(类信息)
- 共享:线程共享
- OOM:
- 元数据区进行垃圾回收后,需要扩容,但是又没有足够的内存扩容时,会导致OOM
- 动态代理无限生成代理类,会导致OOM
- JSP文件运行时,生成大量的类文件
- 特点:如果不设置上限,则默认剩余的机器内存有多大,我就有多大。
- 存储:
- 直接内存:
- 存储:通过NIO直接分配对外内存
- 共享:在于对应的DirectByteBuffer的引用是否共享
- OOM:机器内存不足时,会OOM。
- 特点:不属于运行时数据区,并且OOM时,是因为系统判定要申请的内存不足,并不是自己去申请内存申请不到。
0102.特殊情况
010201 对象只是几乎全部在堆中,并非真的全部在堆中:
- 原因:逃逸分析
- 解释:
- 逃逸分析主要是用来判定指针或者对象,被使用的方法
- 举个例子:我们定义了一个局部对象,但是放到了全局数组里,那么就逃逸出了当前线程。
- 如果判断对象没有逃逸出当前线程,那么就可以把对象分配到栈空间,在线程声明周期结束的时候,直接回收栈空间,就避免了GC,这个分配到栈空间的行为就叫做栈上分配。
- 如果对象没有逃逸出当前线程,并且对象本身并没有被使用,只是对象中的部分属性被使用,则只会在栈上创建对象被使用的部分属性,而不会创建整个对象,这个现象叫做标量替换。
示例代码
public class Test {
public static final List<User> USER_LIST = new ArrayList<User>();
/**
* 线程逃逸
*/
public void show(){
User user = new User();
USER_LIST.add(user);
}
/**
* 方法逃逸
*/
public User show1(){
User user = new User();
return user;
}
/**
* 栈上分配
*/
public void see(){
User user = new User();
System.out.println(user.getClass().getName());
}
/**
* 标量替换
*/
public void seeId(){
User user = new User();
System.out.println(user.id);
}
}
class User{
int id;
}
010202 从G1垃圾收集器开始,分代回收思想就不再是唯一的堆内存布局
堆分为年轻代和老年代;年轻代分为Eden区和S0、S1区;Eden区中又包含TLAB区;TLAB区是线程私有的。
上边这种说法就不完全正确了。
0103.制造OOM
010301. 栈深度超出导致SOF
public class SOFFromStack {
public static void main(String[] args) {
show();
}
public static void show(){
show();
}
}
Exception in thread "main" java.lang.StackOverflowError
at com.memorys.mybook.coding.jvm.oom.SOFFromStack.show(SOFFromStack.java:15)
at com.memorys.mybook.coding.jvm.oom.SOFFromStack.show(SOFFromStack.java:15)
at com.memorys.mybook.coding.jvm.oom.SOFFromStack.show(SOFFromStack.java:15)
010302. 没有足够的内存来创建新线程
public class OOMFromStack {
public static void main(String[] args) {
System.out.println("---start--");
show();
System.out.println("---end--");
}
/**
*
* 没有足够的内存来申请新线程
*/
public static void show(){
while (true){
final Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("new Thread " + Thread.currentThread().getName());
while (true){
}
}
});
thread.start();
}
}
}
经常会导致机器卡死
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread
010303. 堆内存不足溢出
public class HeapOOM {
/**
* @param args -Xms20M -Xmx20M
*/
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
while (true){
list.add(new Object());
}
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
010304. 元空间溢出
public class MetaspaceOOM {
/**
* @param args -XX:MaxMetaspaceSize=10M
* 利用Cglib不断生成代理类来增大元空间占用的内存
*/
public static void main(String[] args) {
while (true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMClass.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invoke(0,objects);
}
});
enhancer.create();
}
}
}
class OOMClass{
}
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:117)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.memorys.mybook.coding.jvm.oom.MetaspaceOOM.main(MetaspaceOOM.java:27)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:459)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
... 6 more
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
... 11 more
010305. 直接内存溢出
public class DiretMemoryOOM {
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(1024 * 1024 * 1024);
}
}
}
特点:没有明显的OOM错误信息,堆内存Dump文件无异常,
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.memorys.mybook.coding.jvm.oom.DiretMemoryOOM.main(DiretMemoryOOM.java:19)