• 从几个sample来学习Java堆,方法区,Java栈和本地方法栈


    最近在看《深入理解Java虚拟机》,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构:

    感觉有必要通过几个实在的例子来加深对这几个区域的了解

    1)Java堆

    所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节,sample如下所示:

    [java] view plaincopy
    1. public class HeapOOM {  
    2.       
    3.     static class OOMObject{}  
    4.   
    5.     /** 
    6.      * @param args 
    7.      */  
    8.     public static void main(String[] args) {  
    9.         List<OOMObject> list = new ArrayList<OOMObject>();  
    10.           
    11.         while(true){  
    12.             list.add(new OOMObject());  
    13.         }  
    14.     }  
    15.   
    16. }  

    加上JVM参数-verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError,就能很快报出OOM:

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    并且能自动生成Dump。

    2)方法区

    方法区是存放虚拟机加载类的相关信息,如类、静态变量和常量,大小由-XX:PermSize和-XX:MaxPermSize来调节,类太多有可能撑爆永久带:

    [java] view plaincopy
    1. public class MethodAreaOOM {  
    2.       
    3.     static class OOMOjbect{}  
    4.   
    5.     /** 
    6.      * @param args 
    7.      */  
    8.     public static void main(String[] args) {  
    9.         // TODO Auto-generated method stub  
    10.         while(true){  
    11.             Enhancer eh = new Enhancer();  
    12.             eh.setSuperclass(OOMOjbect.class);  
    13.             eh.setUseCache(false);  
    14.             eh.setCallback(new MethodInterceptor(){  
    15.   
    16.                 @Override  
    17.                 public Object intercept(Object arg0, Method arg1,  
    18.                         Object[] arg2, MethodProxy arg3) throws Throwable {  
    19.                     // TODO Auto-generated method stub  
    20.                     return arg3.invokeSuper(arg0, arg2);  
    21.                 }  
    22.                   
    23.             });  
    24.             eh.create();  
    25.         }  
    26.     }  
    27.   
    28. }  

    加上永久带的JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后会报如下异常:

    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

    静态变量或常量也会有可能撑爆方法区:

    [java] view plaincopy
    1. public class ConstantOOM {  
    2.   
    3.     /** 
    4.      * @param args 
    5.      */  
    6.     public static void main(String[] args) {  
    7.         // TODO Auto-generated method stub  
    8.         List<String> list = new ArrayList<String>();  
    9.         int i=0;  
    10.         while(true){  
    11.             list.add(String.valueOf(i++).intern());  
    12.         }  
    13.     }  
    14.   
    15. }  

    同样加上JVM参数:-XX:PermSize=10M -XX:MaxPermSize=10M,运行后报如下异常:

    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

    3)Java栈和本地方法栈

    栈是存放线程调用方法时存储局部变量表,操作,方法出口等与方法执行相关的信息,栈大小由Xss来调节,方法调用层次太多会撑爆这个区域,samples如下所示:

    [java] view plaincopy
    1. package com.cutesource;  
    2.   
    3. public class StackOOM {  
    4.   
    5.     /** 
    6.      * @param args 
    7.      */  
    8.       
    9.     private int stackLength = 1;  
    10.       
    11.     public void stackLeak(){  
    12.         stackLength++;  
    13.         stackLeak();  
    14.     }  
    15.       
    16.     public static void main(String[] args) throws Throwable{  
    17.         // TODO Auto-generated method stub  
    18.         StackOOM oom = new StackOOM();  
    19.         try{  
    20.             oom.stackLeak();  
    21.         }catch(Throwable err){  
    22.             System.out.println("Stack length:" + oom.stackLength);  
    23.             throw err;  
    24.         }  
    25.           
    26.     }  
    27.   
    28. }  

    设置JVM参数:-Xss128k,报出异常:

    Exception in thread "main" java.lang.StackOverflowError

    打印出Stack length:1007,这里可以看出,在我的机器上128k的栈容量能承载深度为1007的方法调用。当然报这样的错很少见,一般只会出现无限循环的递归中,另外,线程太多也会占满栈区域:

    [java] view plaincopy
    1. package com.cutesource;  
    2.   
    3. public class StackOOM {  
    4.   
    5.     /** 
    6.      * @param args 
    7.      */  
    8.       
    9.     private int stackLength = 1;  
    10.       
    11.     private void dontStop(){  
    12.         while(true){  
    13.             try{Thread.sleep(1000);}catch(Exception err){}  
    14.         }  
    15.     }  
    16.       
    17.     public void stackLeakByThread(){  
    18.         while(true){  
    19.             Thread t = new Thread(new Runnable(){  
    20.   
    21.                 @Override  
    22.                 public void run() {  
    23.                     // TODO Auto-generated method stub  
    24.                     dontStop();  
    25.                 }  
    26.                   
    27.             });  
    28.             t.start();  
    29.             stackLength++;  
    30.         }  
    31.     }  
    32.       
    33.     public static void main(String[] args) throws Throwable{  
    34.         // TODO Auto-generated method stub  
    35.         StackOOM oom = new StackOOM();  
    36.         try{  
    37.             oom.stackLeakByThread();  
    38.         }catch(Throwable err){  
    39.             System.out.println("Stack length:" + oom.stackLength);  
    40.             throw err;  
    41.         }  
    42.           
    43.     }  
    44.   
    45. }  

    报出异常:Exception in thread "main" java.lang.OutOfMemoryError:unable to create new native thread

    不过在windows上运行这个例子要小心,会出现系统假死的情况,有可能需要重启机器才行。

    以上几个例子虽然比较简单,但能很好帮助普通的程序员更加直观的了解Java堆方法区Java栈和本地方法栈

  • 相关阅读:
    2w字 + 40张图带你参透并发编程!
    完了,这个硬件成精了,它竟然绕过了 CPU...
    一文详解 Java 并发模型
    详解匈牙利算法与二分图匹配
    机器学习 | 详解GBDT在分类场景中的应用原理与公式推导
    Python | 浅谈并发锁与死锁问题
    LeetCode 91,点赞和反对五五开,这题是好是坏由你来评判
    LeetCode 90 | 经典递归问题,求出所有不重复的子集II
    【Azure DevOps系列】什么是Azure DevOps
    MSIL入门(四)之委托delegate
  • 原文地址:https://www.cnblogs.com/daichangya/p/12958824.html
Copyright © 2020-2023  润新知