Java虚拟机内存溢出

最近在看周志明的《深入理解Java虚拟机》,虽然刚刚开始看,但是觉得还是一本不错的书。对于和我一样对于JVM了解不深,有志进一步了解的人算是一本不错的书。注明:不是书托,同样是华章出的书,质量要比《深入剖析Tomcat》高好多,起码排版上没有那么多严重的失误,停,等哪天心情不好再喷那本书。:)(还有一本书让我看完觉得挺不爽的,当然不排除自身问题)

刚刚看了两章,第一章我比较关注如何自己编译openJdk,额,现在还没捣腾成功,完成后再分享,暂且跳过;本篇文章的主要任务是记录书中关于产生OutOfMemoryError异常的原因。代码以及说明基本都是出自原书,写这篇文章意在加深印象,同时分享给那些没有读过这本书的人。说句自己的一次经历,不记得是在哪家公司面试来着,面试官曾经问过我都有哪些情况会造成OutOfMemoryError异常。很遗憾,当时我不会。

设置运行时参数

说下为什么加了这样一节,说来惭愧,第一次设置运行时参数,找不到在哪里设置,找了半天才找对位置,怕有和我一样小白的人存在,所以就增加了这样一个小节。(IDE工具是eclipse)

按照如下三步设置即可,呈现一场代码的注释中会标注每种情形需要设置的运行时参数。

step1:
step2:
step3:

可以这样为每个含有main函数的类指定自己的运行时参数。

造成内存溢出之五大元凶

个人觉得程序员都要有”刨祖坟”的精神,文艺一点儿就是知其然,知其所以然。在日常的工作中更应该如此,不能说要实现一个功能就满口答应,起码要知道为什么需要这样一个功能,解决什么问题,是否合理。如果连原因都不知道,真心不相信能把这个功能做好。也许这个也是好管理和不好管理程序员的分割线。如果说发生OutOfMemoryError跟我们无关,那我们为什么要知道发生的原因啊,美国打伊拉克我和程序员有毛关系啊。其实这个异常对大家来说应该都不陌生,之前我最爱的处理就是从新再运行一次,不行关闭eclipse,再不行重启电脑。(杀手锏级别的解决方案).可是这样不科学,科学的方式就要求我们知道为什么会发生这个异常,换句话说是发生这个异常的场景,然后通过打印出的异常信息快速定位发生内存溢出的区域,然后进行权衡,调整运行时参数来解决。

Java堆溢出

Java堆用于存储对象实例,知道这一点就很容易呈现堆溢出,不断的创建对象,并且保持有指向其的引用,防止为gc。

代码如下:

  import java.util.ArrayList;
import java.util.List;
/**
   * VM Args:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
   *
   */
public class HeapOOM {
static class OOMObject{
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while(true){
list.add(new OOMObject());
}
}
}

通过设置-Xms20M -Xmx20M都为20M意在防止堆大小自动扩展,更好的展现溢出。 执行结果如下:

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

是不是很明显啊,显示堆空间发生OutOfMemoryError。

书中告诉我们发生了OutOfMemoryError后,通常是通过内存影像分析工具对dump出来的堆转储快照进行分析(这就是运行时参数中配置-XX:+HeapDumpOnOutOfMemoryError的原因),重点是确定是由内存泄露(Memory Leak)还是有内存溢出(Memory Overflow)引起的OutOfMemoryError。如果是内存泄露则找到泄露点,修正;如果确实是合理的存在,那么就增加堆空间。(内存分析这里我也木有做过,工具也木有使用过,在后续章节会有介绍,用熟了后再来一篇)

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

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法区栈,因此对于HotSpot来说,-Xoss(设置本地方法栈大小)参数是无效的,栈容量由-Xss参数设定。关于虚拟机栈和本地方法区栈,在Java虚拟机规范中描述了两种异常:

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

    书中谈到单线程的场景下只能浮现StackOverflowError,那我们就先来看看单线程场景下到底会是什么样子。

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

    通过-Xss128k设置虚拟机栈大小为128k,执行结果如下:

    执行结果显示,确实是发生了StackOverflowError异常。

    通过不断创建线程耗尽内存也可以呈现出OutOfMemoryError异常,但是在Windows平台下模拟会使系统死机,我这里就不多说了。感兴趣的可以自己去尝试。

    运行时常量池溢出

    向运行时常量池中添加内容最简单的方式就是使用String.intern()方法。由于常量池分配在方法区内,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

    代码如下:

      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中将永久区(方法区和常量池)给干掉了,没有验证过。永久区可以说是在堆之上的一个逻辑分区。如果jdk7中去掉了,那么这个示例应该会抛出堆空间的内存溢出,而非运行时常量池的内存溢出。所以在执行程序的时候分别用了jdk6和jdk7两个版本。多说一句,如果jdk7去掉了方法区,那么-XX:PermSize=10M -XX:MaxPermSize=10M就不起作用了,所以在jdk7环境下运行时,堆大小为jvm默认的大小,要执行一会儿(半小时左右:( ))才能抛出异常。没关系,再配置下运行时参数即可,注意要配置成不可扩展。以图为据:

  • jdk6环境下抛出运行时常量池内存溢出
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

    显而易见PermGen space,永久区。不解释。

  • jdk7环境下,运行时参数为:-XX:PermSize=10M -XX:MaxPermSize=10M
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    运行了好久好久,最终抛出堆内存溢出。Java heap space已经足够说明问题了。

  • jdk7环境下,运行时参数为:-verbose:gc -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    同样也是堆内存溢出,不过速度就快了好多好多,因为堆大小被设置为不可扩展。

    方法区溢出

    方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。测试这个区域只要在运行时产生大量的类填满方法区,知道溢出。书中借助CGlib直接操作字节码运行时,生成了大量的动态类。

    当前主流的Spring和Hibernate对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载到内存。

    测试代码如下:

      import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    /**
       * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
       *
       */
    public class JavaMethodAreaOOM {
    public static void main(String[] args) {
    while (true) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(OOMObject.class);
    enhancer.setUseCache(false);
    enhancer.setCallback(new MethodInterceptor() {
    public Object intercept(Object object, Method method,
    Object[] args, MethodProxy proxy) throws Throwable{
    return proxy.invokeSuper(object, args);
    }
    });
    enhancer.create();
    }
    }
    static class OOMObject {
    }
    }
    

    工程中要引入cglib-2.2.2.jar和asm-all-3.3.jar。

    方法区的内存溢出问题同样存在jdk6和jdk7版本之间的区别,同运行时常量池内存溢出。

    方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收掉,判定条件是非常苛刻的。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用了CGLib字节码增强外,常见的还有:大量JSP或动态生成JSP文件的应用、基于OSGi的应用等。

    本机直接内存溢出

    DirectMemory容量可以通过-XX:MaxDirectMemorySize指定。

    示例代码如下:

      import java.lang.reflect.Field;
    import sun.misc.Unsafe;
    /**
       * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
       *
       */
    public class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;
    /**
         * @param args
         * @throws IllegalAccessException
         * @throws IllegalArgumentException
         */
    public static void main(String[] args) throws IllegalArgumentException,
    IllegalAccessException {
    // TODO Auto-generated method stub
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe) unsafeField.get(null);
    while(true){
    unsafe.allocateMemory(_1MB);
    }
    }
    }
    

    运行结果如下图:

    抛出内存溢出异常。不解释。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用户输入汉字时计算机首先将,用户输入汉字时,计算机首先将汉字的输入码转换为__________。...

用户的蓄的形能器常见式有。输入时计算机首先输入包括药物具有基的酚羟。汉字换物包腺皮括质激肾上素药。对既荷又有线有相间负负荷时&#xff0c;将汉倍作为等选取相负效三相负荷乘荷最大&#xff0c;将汉相负荷换荷应先将线间负算为&#xff0c;效三相负荷时在计算等&#xf…

从最终用户角度来看外部结构_从不同角度来看您最喜欢的游戏

从最终用户角度来看外部结构The complete python code and Exploratory Data Analysis Notebook are available at my github profile;完整的python代码和Exploratory Data Analysis Notebook可在我的github个人资料中找到 &#xff1b; Pokmon is a Japanese media franchise,…

apache+tomcat配置

无意间看到tomcat 6集群的内容&#xff0c;就尝试配置了一下&#xff0c;还是遇到很多问题&#xff0c;特此记录。apache服务器和tomcat的连接方法其实有三种:JK、http_proxy和ajp_proxy。本文主要介绍最为常见的JK。 环境&#xff1a;PC2台&#xff1a;pc1(IP 192.168.88.118…

记自己在spring中使用redis遇到的两个坑

本人在spring中使用redis作为缓存时&#xff0c;遇到两个坑&#xff0c;现在记录如下&#xff0c;算是作为自己的备忘吧&#xff0c;文笔不好&#xff0c;望大家见谅&#xff1b; 一、配置文件 1 <!-- 加载Properties文件 -->2 <bean id"configurer" cl…

Azure实践之如何批量为资源组虚拟机创建alert

通过上一篇的简介&#xff0c;相信各位对于简单的创建alert&#xff0c;以及Azure monitor使用以及大概有个印象了。基础的使用总是非常简单的&#xff0c;这里再分享一个常用的alert使用方法实际工作中&#xff0c;不管是日常运维还是做项目&#xff0c;我们都需要知道VM的实际…

管道过滤模式 大数据_大数据管道配方

管道过滤模式 大数据介绍 (Introduction) If you are starting with Big Data it is common to feel overwhelmed by the large number of tools, frameworks and options to choose from. In this article, I will try to summarize the ingredients and the basic recipe to …

DevOps时代,企业数字化转型需要强大的工具链

伴随时代的飞速进步&#xff0c;中国的人口红利带来了互联网业务的快速发展&#xff0c;巨大的流量也带动了技术的不断革新&#xff0c;研发的模式也在不断变化。传统企业纷纷效仿互联网的做法&#xff0c;结合DevOps进行数字化的转型。通常提到DevOps&#xff0c;大家浮现在脑…

用户体验可视化指南pdf_R中增强可视化的初学者指南

用户体验可视化指南pdfLearning to build complete visualizations in R is like any other data science skill, it’s a journey. RStudio’s ggplot2 is a useful package for telling data’s story, so if you are newer to ggplot2 and would love to develop your visua…

linux挂载磁盘阵列

linux挂载磁盘阵列 在许多项目中&#xff0c;都会把数据存放于磁盘阵列&#xff0c;以确保数据安全或者实现负载均衡。在初始安装数据库系统和数据恢复时&#xff0c;都需要先挂载磁盘阵列到系统中。本文记录一次在linux系统中挂载磁盘的操作步骤&#xff0c;以及注意事项。 此…

sql横着连接起来sql_SQL联接的简要介绍(到目前为止)

sql横着连接起来sqlSQL Join是什么意思&#xff1f; (What does a SQL Join mean?) A SQL join describes the process of merging rows in two different tables or files together.SQL连接描述了将两个不同表或文件中的行合并在一起的过程。 Rows of data are combined bas…

《Python》进程收尾线程初识

一、数据共享 from multiprocessing import Manager 把所有实现了数据共享的比较便捷的类都重新又封装了一遍&#xff0c;并且在原有的multiprocessing基础上增加了新的机制list、dict 机制&#xff1a;支持的数据类型非常有限 list、dict都不是数据安全的&#xff0c;需要自己…

北京修复宕机故障之旅

2012-12-18日 下午开会探讨北京项目出现的一些问题&#xff0c;当时记录的问题是由可能因为有一定数量的客户上来后&#xff0c;就造成了Web服务器宕机&#xff0c;而且没有任何时间上的规律性&#xff0c;让我准备出差到北京&#xff0c;限定三天时间&#xff0c;以及准备测试…

一般线性模型和混合线性模型_从零开始的线性混合模型

一般线性模型和混合线性模型生命科学的数学统计和机器学习 (Mathematical Statistics and Machine Learning for Life Sciences) This is the eighteenth article from the column Mathematical Statistics and Machine Learning for Life Sciences where I try to explain som…

《企业私有云建设指南》-导读

内容简介第1章总结性地介绍了云计算的参考架构、典型解决方案架构和涉及的关键技术。 第2章从需求分析入手&#xff0c;详细讲解了私有云的技术选型、资源管理、监控和运维。 第3章从计算、网络、存储资源池等方面讲解了私有云的规划和建设&#xff0c;以及私有云建设的总体原则…

太原冶金技师学院计算机系,山西冶金技师学院专业都有什么

山西冶金技师学院专业大全大家在考试之后对除了选择学校之外&#xff0c;还更关注专业的选择&#xff0c;山西冶金技师学院有哪些专业成了大家最为关心的问题。有些同学一般是选择好专业再选择自己满意的学校&#xff0c;下面小编将为你介绍山西冶金技师学院开设的专业及其哪些…

海南首例供港造血干细胞志愿者启程赴广东捐献

海南首例供港造血干细胞志愿者启程赴广东捐献。 张瑶 摄 海南首例供港造血干细胞志愿者启程赴广东捐献。 张瑶 摄 中新网海口1月23日电 (张茜翼 张瑶)海南省首例供港造血干细胞捐献者晶晶(化名)23日启程赴广东进行捐献&#xff0c;将于28号正式捐献采集造血干细胞&#xff0c;为…

如何击败Python的问题

Following the previous article written about solving Python dependencies, we will take a look at the quality of software. This article will cover “inspections” of software stacks and will link a free dataset available on Kaggle. Even though the title say…

KindEditor解决上传视频不能在手机端显示的问题

KindEditor自带的上传视频生成的HTML代码为<embed>&#xff0c;在手机端并不支持。于是可以自己在控件里增加生成video标签相关代码。 参考https://www.jianshu.com/p/047198ffed92。。 然而对着修改后没有成功&#xff0c;可能是那里没有改对吧。依然生成的是<embed&…

《独家记忆》见面会高甜宠粉 张超现场解锁隐藏技能

1月23日&#xff0c;由爱奇艺出品&#xff0c;小糖人联合出品的沉浸式成长练爱剧《独家记忆》在京举行粉丝见面会。爱奇艺高级副总裁陈宏嘉&#xff0c;爱奇艺副总裁、自制剧开发中心总经理、《独家记忆》总制片人戴莹&#xff0c;小糖人董事长、《独家记忆》总制片人朱振华&am…

计算机软件技术基础fifo算法,软件技术基础真题

一、填空1、数据结构按逻辑结构可分为两大类&#xff0c;它们分别是线性和非线性2.1、在长为n的顺序存储的线性表中插入和删除元素时所需移动元素的平均次数(等概率情况下)为&#xff1a;Einn/2&#xff0c;Ede(n-1)/22.2、顺序表有5个元素&#xff0c;设在任何位置上插入元素是…