zoukankan      html  css  js  c++  java
  • JVM内存相关问题现象

    通常内存溢出时JVM会提示具体的内存溢出原因,下面是几种常见的情况及简要的原因说明及相关的JVM配置。

    栈溢出:StackOverflowError

    JVM输出信息:“java.lang.StackOverflowError”。

    JVM相关机制:JVM在执行Java方法调用时需要使用栈传递调用参数、返回值以及保存局部变量表,通常组织为栈帧(Stack Frame)结构。从概念上说,每次Java方法调用都会消耗一定的栈内存,当这个方法调用结束返回后释放这部分内存。而每个Java线程对应的栈内存是有限的(通常JVM启动后就固定了),因此当方法嵌套层数过多或者栈帧内的数据结构(如局部变量表)过大时,可能出现StackOverflowError。

    JVM控制参数:可以通过-Xss1024K设置线程栈大小为1024K。通常无需设置此参数,在不同OS下的JVM均有自己的默认值设置,一般在256K-1024K之间。

    常见原因分析建议:出现此问题通常应该先分析应用系统的原因,而不是考虑增大“-Xss”参数设置值。因为出现此问题的最可能原因是过深的方法调用(比如错误的递归调用造成方法调用层次过深),即应用程序中的编程错误造成栈溢出。当出现StackOverflowError时,根据此异常的详细信息通常可以比较明确地找到错误代码的可能位置,通常不需要复杂的工具支持。

    堆溢出

    JVM输出信息“java.lang.OutOfMemoryError: Java heap space”,或者类似输出信息,视JVM厂商及版本略有区别,但关键字都是“heap”或者“堆”。

    JVM相关机制:从概念上说,Heap是存储Java类实例和数组的内存区。比如任何“obj=new XClass();”的调用都是在Heap中进行内存分配,当不能成功进行这种内存分配时,JVM将抛出“java.lang.OutOfMemoryError: Java heap space”。

    JVM控制参数

    -Xms512m:设置Heap最小值为512m;

    -Xmx1024m:设置Heap最大值为1024m;

    JVM控制参数设置相关知识及建议

    在生产环境中可以将-Xms和-Xmx设为相同大小,避免JVM动态调整Heap大小;

    在32位系统上(无论是Windows还是Unix/Linux),-Xmx参数通常无法设置到大于1500m,且此值在不同的系统上并不相同;

    在32位系统上,并不建议将-Xmx设到最大(如1500m)。因为32位系统中JVM进程的可用的内存是有限的(一个32为系统上的进程的地址空间最大为4G,能使用的内存最大为4G,实际上OS核心会占据1~2G地址空间,而留给应用层的地址空间通常不超过3G,应用层真正能够使用的通常在2G以内),这些内存除了Java Heap使用,JVM进程本身、Native Thread都需要使用。因此过大的Java Heap可能增大其他溢出的可能;

    常见原因分析建议:Java堆内存溢出是较为常见的情况,有可能是因为:

      应用系统的错误导致的问题,即应用系统错误地生成了太多的新Object,JVM中用来存储对象的堆(heap)中无法容纳新Object。此时需要通过分析Heap Dump(堆转储文件)定位并修正问题;

      也有可能并非程序错误,而是业务场景或者并发程度确实需要更大的Heap来支持。此时有多种可能的解决方案:

      分析Heap Dump(堆转储文件),确定应用中内存使用情况,更改应用的实现逻辑,减少新Object的创建。其本质是以时间换空间的策略,即增加应用逻辑执行时间,来换取更少的内存需求;

      增大Heap,即设置更大的-Xmx参数。32位系统上余地并不大,并可能增大Thread相关内存溢出的可能性;

      更换为使用64位系统,64位系统才能支持更大的Heap,但增大Heap带来的不仅是好处,也可能造成GC时间增加,应用系统“停顿”的感觉更明显;

    堆内存溢出较为常见,准确地定位及分析需要借助工具,后文将详细描述如何借助工具进行分析。

    持久区(方法区)溢出

    JVM输出信息:“java.lang.OutOfMemoryError: PermGen space”。

    JVM相关机制:概念上说,JVM持久区(方法区)是用来存储类型相关的信息, 如该类型的常量池,字段或方法信息,而且类型中的类(静态)变量同样也是存储在方法区中(如到ClassLoader的引用和到Class类的引用)。当持久区不足以容纳需要加载的Class时JVM将抛出此异常,异常中明确指出是持久区内存不足。

    JVM控制参数

    -XX:PermSize=64m:设置初始持久区大小为64m;

    -XX:MaxPermSize=256m:设置持久区最大为256m;

    常见原因分析建议:此问题通常是太多的Class需要加载造成的,Class的数量和应用系统的代码规模相关,较大的应用系统需要较大的MaxPermSize设置。需要注意的是,Java EE应用中的jsp最终会被编译成Class并加载,因此可能在大量使用jsp的Java EE应用系统中需要设置较大的MaxPermSize,但通常不应该超过256m。另外,对于使用了ASM/CGLib等字节码工具动态生成Class的系统,编程错误导致的运行时大量生成Class也可能导致此异常。此异常明确地说明了问题,且除了编程错误一般都能够通过加大MaxPermSize参数值来解决,通常不需要复杂的工具进行分析定位。

    无法创建OS本地线程

    JVM输出信息:“java.lang.OutOfMemoryError: unable to create new native thread”。

    JVM相关机制:概念上说,JVM中启动一个Thread,通常会在OS中创建并启动一个本地线程(Native Thread),而无论是因为内存问题还是本地线程数量问题,都有可能导致此异常。

      Native Thread内存问题:OS在创建本地线程时同样需要使用内存,但这个内存不在JVM的堆内存范围内,而是在JVM进程之内、Java Heap/方法区等内存之外。因此完全有可能Java Heap越大,剩余的用来创建Native Thread的内存越小,从而可创建的Native Thread越少,出现此异常的可能性越大;

      Native Thread数量问题:OS对于进程允许创建的线程数量通常有限制,因此JVM能够启动的Thread数量也有限,当JVM中启动的Thread超出此限制时将出现此异常。通常JVM中已经启动的Thread数量应该是有限的,除非是程序错误启动了大量的Thread。

    JVM控制参数:无,但通常减少-Xmx的值能够保留更多的内存给OS线程。

    常见原因分析建议:出现此问题时,只要获取JVM当时的Thread Dump(线程转储信息),并对Thread Dump进行分析即可。分析Thread Dump信息很容易获取JVM线程相关的信息,判断JVM中是否存在预料之外的线程,或者线程所处的状态和正在执行的调用是否符合预期。

    内存泄漏

    严格意义上来说,我们一般遇到的JVM中的内存泄漏其实不是内存泄漏,因为严格地说内存泄漏指的是已经分配的内存无法被应用系统正确回收。而我们通常说的JVM中的内存泄漏只是部分对象被其他对象持有引用,因此不能被GC过程回收;同时,这些不能回收的对象被应用系统“遗忘”了,不再有任何用处的同时还占据着大量内存。在应用系统看来这部分内存像是丢失了(或者泄露了)。我们解决内存泄漏问题就是要找到的是这些应用程序中的错误。

    举个简单的例子:

    Map globalMap = new HashMap<String, List<SomeObject>>();

    globalMap在整个应用系统生命周期内都会被引用,因此不能被GC回收。因而任何globalMap.put(aString, aList)的操作将在globalMap中引用aString和aList两个对象,直到被明确清除出globalMap后aString和aList才有可能被GC回收,否则此两个对象将永远占据JVM内存。如果应用系统是“忘了”从globalMap中remove(aString),则对应用系统来说就像是aString和aList所占的内存“泄漏”了。

  • 相关阅读:
    python通过scapy编写arp扫描器
    red hat重置密码
    浅谈跨域劫持
    python中的socket
    利用Python进行端口扫描
    自己搜罗了一些感觉蛮有意思的爬虫相关的网站
    与py2neo的第一次接触
    基本上所有的库的列表都在这里了,传送门~~~
    关于neo4j初入门(5)
    关于neo4j初入门(4)
  • 原文地址:https://www.cnblogs.com/xiongmaotailang/p/5249543.html
Copyright © 2011-2022 走看看