• Java虚拟机中各运行时区域OOM实例


    Java堆溢出

      只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量之后就会产生内存溢出异常

     1 public class HeapOOM{
     2       static class OOMObject{
     3       }
     4       
     5       public static void main(String[] args){
     6            List<OOMObject> list = new ArrayList<OOMObject>();
     7            while(true){
     8                    list.add(new OOMObject()):
     9            }   
    10      }    
    11 }

      解决堆内的异常,一般手段先通过内存映像分析工具(如Eclipse Memory Analyzer)对Dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏还是内存溢出。

    如果是内存泄漏, 可以进一步通过工具查看泄漏对象到GC Roots的引用链,于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们。

    如果不存在内存泄漏,就是内存中的对象确实都还必须活着,那就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

    虚拟机栈和本地方法栈溢出

      栈容量只由-Xss参数设定,在Java虚拟机规范中描述了两种异常:

    •   如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError异常
    •        如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

      

    public class JavaVMStackSOF{
        private int stackLength = 1;
        public void stackLeak(){
            stackLength++;
            stackLeak();
        }
        
        public static void main(String[] args) {
            JavaVMStackSOF oom = new JavaVMStackSOF();
            try{
                oom.stackLeak();
            }catch(Throwable e){
                System.out.println("stack length:"+oom.stackLength);
                throw e;
            }
        }
    }

       上述是在单线程情况下,无论是由于栈帧太大还是虚拟机栈容量太小,抛出的都是StackOverFlowError。还有一种因为创建线程导致的内存溢出异常,代码如下:

    public class JavaVMStackOOM{    
        private void dontStop(){
            while(true){
                
            }
        }
        
        public void stackLeakByThread(){
            while(true){
                Thread thread = new Thread(new Runnable(){
                    @Override
                    public void run() {
                        dontStop();                
                    }    
                });
                thread.start();
            }
        }
        
        public static void main(String[] args) {
            JavaVMStackOOM oom = new JavaVMStackOOM();
            oom.stackLeakByThread();
        }
    }

    方法区和运行时常量池溢出

      首先介绍String.intern()这个本地方法,它的作用是:如果字符串常量池中已经包含一个此String对象的字符串,则返回代表池中这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。

      我们可通过-XX: PermSize 和 -XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量

    public class RuntimeConstantPoolOOM{    
        public static void main(String[] args) {
            //使用List保持着常量池引用,避免Full GC回收常量池行为
            List<String> list = new ArrayList<String>();
            int i = 0;
            while(true){
                list.add(String.valueOf(i++).intern());
            }
        }
    }

     

      

      

  • 相关阅读:
    编译原理:正则表达式
    编译原理:基于状态转换图识别for语句
    汇编小笔记
    win10汇编如何debug(小白向)
    nodejs模块引用
    nodejs环境设置理解
    分享几个看上去很酷炫的运算
    JavaScript数据结构 (手打代码)
    爬取煎蛋XXOO妹子图片
    Python Selenium官方文档阅读笔记
  • 原文地址:https://www.cnblogs.com/Edword-ty/p/10526239.html
Copyright © 2020-2023  润新知