图片-小白之家
图片-小白之家
图片-小白之家
图片-小白之家

java heap space(解决方法)

JVM的OOM分为多种状况,下面会针对java.lang.OutOfMemoryError:Javaheapspace这种状况解说一下产生的原因与解决方案。

在JAVA运用启动时,会限制运用的运用空间。也就说,任何一个JAVA运用,都只能运用有限的内存空间。

JAVA的内存空间在JDK7及曾经划分为堆与永久代。在JDK8之后移除了永久代,选用元空间来代替。

在启动时,经过指定JVM参数:`-Xmx`来设置可运用的最大堆巨细。假如没有显式的设置,则系统上默以为物理内存的1/4(依据物理内存的不同状况有不同的分配规则。可是遍及能够以为是1/4)。

产生java.lang.OutOfMemoryError:Javaheapspace反常时,代表着运用尝试从堆上申请一个区域时,堆没有可配的空间。(注:可能有可运用的物理内存,可是没有现已达到了JAVA运用可分配的内存巨细)

JVM是很智能的,在即将产生OOM时,会进行一次FullGC以收回可收回的目标来释放空间。假如FullGC之后仍是没有可满意巨细的空间分配,才抛出java.lang.OutOfMemoryError:Javaheapspace。

java.lang.OutOfMemoryError:Javaheapspace正常是怎么产生的呢?

突发高峰期:程序在正常的用户量和必定数据量时运转正常。可是,在某个高峰时导致超出预期阈值,内存存活目标运用空间的量超出最大堆,并且无法收回。内存走漏:因为编程错误导致运用程序不再需求的目标(数据)一向被持有引证,导致无法被收回。随着时刻的推移,走漏的内存目标占用了一切的可用堆空间。分配合理的满意内存

最简略的解决方法就给JVM分配满意大的内存来满意运转程序的需求。

可是,需求留意在内存走漏的状况下,分配再大的内存也只是推迟了java.lang.OutOfMemoryError:Javaheapspace的产生。

并且,加大了JVM堆内存,也会增加在GC时的暂停时刻(STW),影响程序的吞吐量,增加推迟。

怎么分配一个合理的内存空间,是需求针对GC进行优化的。也就是常说的JVM调优。JVM调优能够参考:「JVM」GC——调优介绍

那么,怎么调整经过分配JAVA堆空间来解决问题呢?

首先,需求了解以下这些问题:

哪些目标占用了很多的堆空间在哪些代码中创建了这些目标上述的问题能够经过JVM自身的jmap来dump出运转时的堆栈信息。然后经过如:MAT,JProfiler,jconsole等空间来进行内存目标占用的盯梢。

MAT运用能够参考:[JVM]MAT进阶运用

当然,这种方式是比较原始的方式。主张经过如:Plumbr等JVM监控东西来盯梢问题。

Plumbr的陈述信息

以上图的监控举例扼要阐明一下怎么适当的进行堆空间的巨细分配。

上图所示中,能够得到如下信息:

一切相关目标的整个GCRoot引证内存消耗最多的目标:

这些目标在代码中的分配方位:

依据上述的信息,我们能够得到这样的猜测:

这个程序的需求的运转空间超过248MB,并且是无法在必定时刻内释放被收回。那么,按JVM调优的思路,主张分配的最大堆巨细为:老年代活跃数据巨细*3~4倍。

所以,我们第一次调整时,能够分配:248*4=992。

因为堆巨细的无法确认,所以第一次调整直接调整为:-Xmx1024m。

单位:-Xmx1024即装备1024b=1kb-Xmx1024k即装备1mb-Xmx1024m即装备1gb-Xmx1g即装备1gb主张:在装备-Xmx时,应该将-Xms也装备成相同巨细。避免JVM需求动态调整堆空间巨细带来的功能影响。
图片[1]-java heap space(解决方法)-小白之家,javaheapspace解决方法,在JVM中假如98%的时刻是用于GC(GarbageCollection)且可用的Heapsize不足2%的时分将抛出反常信息,java.lang.OutOfMemoryError:Javaheapspace。

所以产生这个异样的原因一般有两种:

1.程序中出现了死循环

2.程序占用内存太多,超越了JVM堆设置的最大值。

关于第一种状况,需求自己检查程序代码,这里不再多说。

第二种状况,我们手艺扩展JVM堆的参数设置。JVM堆的设置是指java程序运转过程中JVM能够分配运用的内存空间的设置。在JVM发动时,JVM堆会主动设置heapsize值。一般状况下,初始空间(即-Xms)默认值是物理内存的1/64,最大空间是物理内存的1/4。能够运用JVM供给的-Xmn-Xms-Xmx等选项可进行设置。这里对各个参数的含义解释一下:-Xms:初始值-Xmx:最大值-Xmn:最小值HeapSize的设置不宜太小,也不宜太大。若设置太小程序的响应速度会变慢了,由于GC占用了更多的时刻,而运用分配到的履行时刻较少。太大也会形成空间的糟蹋,并且也会影响其他程序的正常运转。HeapSize最大最好不要超越可用物理内存的80%。建议将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。设置的办法首要有以下几个:

1.便是在履行JAVA类文件时加上这个参数,其间className是需求履行确实类名。(包含包名)如:java-Xms32m-Xmx800mclassName这个不只解决问题了,并且履行的速度比没有设置的时分快许多。假如是开发测验,也能够再eclipse中直接设置。Eclipse->run-arguments中的VMarguments中输入-Xms32m-Xmx800m这个参数就能够了。

2.能够在windows更改体系环境变量加上JAVA_OPTS=-Xms64m-Xmx512m。3.假如用的tomcat,在windows下,能够在C:\tomcat5.5.9\bin\catalina.bat(详细途径依据自己tomcat的位置而定)中加上:setJAVA_OPTS=-Xms64m-Xmx256m(巨细依自己内存而定)位置在:remGuessCATALINA_HOMEifnotdefined这行的下面加合适.4.假如是linux体系Linux在{tomcat_home}/bin/catalina.sh的前面,加setJAVA_OPTS=’-Xms64-Xmx512′

由于程序要从数据读取近10W行记载处理,当读到9W的时分就出现java.lang.OutOfMemoryError:Javaheapspace这样的过错。

在网上一查或许是JAVA的仓库设置太小的原因。

跟据网上的答案大致有这两种解决办法:

1、设置环境变量

setJAVA_OPTS=-Xms32m-Xmx512m

能够依据自己机器的内存进行更改,但自己测验这种办法并没有解决问题。或许是还有哪里需求设置。

2、java-Xms32m-Xmx800mclassName

便是在履行JAVA类文件时加上这个参数,其间className是需求履行确实类名。(包含包名)

这个解决问题了。并且履行的速度比没有设置的时分快许多。

假如在测验的时分或许会用Eclispe这时分就需求在Eclipse->run-arguments中的VMarguments中输入-Xms32m-Xmx800m这个参数就能够了。

java.lang.OutOfMemoryError:Javaheapspace

===================================================

运用Java程序从数据库中查询大量的数据时出现反常:

java.lang.OutOfMemoryError:Javaheapspace

在JVM中假如98%的时刻是用于GC且可用的Heapsize不足2%的时分将抛出此反常信息。

JVM堆的设置是指java程序运转过程中JVM能够分配运用的内存空间的设置.

JVM在发动的时分会主动设置Heapsize的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。能够运用JVM供给的-Xmn-Xms-Xmx等选项可进行设置。

例如:java-jar-Xmn16m-Xms64m-Xmx128mMyApp.jar

假如HeapSize设置偏小,除了这些反常信息外,还会发现程序的响应速度变慢了。GC占用了更多的时刻,而运用分配到的履行时刻较少。

HeapSize最大不要超越可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

Heapsize的-Xms-Xmn设置不要超出物理内存的巨细。不然会提示“ErroroccurredduringinitializationofVMCouldnotreserveenoughspaceforobjectheap”。

==========================================================

通过一个晚上的尽力终于完成了一个文件替换指定字符串的程序,可是由于我要替换的全站程序html文件太多,所以eclipse下边老是在一个目录完毕后报出java.lang.OutOfMemoryError:Javaheapspace的反常,然后就溃散了。

我一想肯定是频频操作形成来不及收回,所以在每个循环之后加上一个Thread.sleep(1000),发现仍是到那个目录下就死掉,所以把1000改成5000,仍是到那里死掉,我想或许不是来不及收回这么简略,或许sun的JVM里面刚好关于这种状况不释放也有或许。

接着我又把发动的参数添上一个-Xmx256M,这回就能够了。

想一想,仍是关于废物收回的原理不太了解,就在网上查了一下,发现了几篇不错的文章。

http://java.ccidnet.com/art/3539/20060314/476073_1.html

http://www.pconline.com.cn/pcedu/empolder/gj/java/0509/701281.html

还有:Java堆的管理—废物收回提到一下几点,很不错,或许能够作为写程序时分的原则:

(1)不要试图去假定废物搜集产生的时刻,这一切都是未知的。比方,办法中的一个暂时目标在办法调用完毕后就变成了无用目标,这个时分它的内存就能够被释放。

(2)Java中供给了一些和废物搜集打交道的类,并且供给了一种强行履行废物搜集的办法–调用System.gc(),但这同样是个不确定的办法。Java中并不确保每次调用该办法就必定能够发动废物搜集,它只不过会向JVM宣布这样一个请求,到底是否真实履行废物搜集,一切都是个未知数。

(3)挑选合适自己的废物搜集器。一般来说,假如体系没有特殊和严苛的性能要求,能够采用JVM的缺省选项。不然能够考虑运用有针对性的废物收集器,比方增量搜集器就比较合适实时性要求较高的体系之中。体系具有较高的配置,有比较多的搁置资源,能够考虑运用并行标记/铲除搜集器。

(4)关键的也是难把握的问题是内存泄漏。良好的编程习惯和谨慎的编程态度永远是最重要的,不要让自己的一个小过错导致内存出现大缝隙。

(5)尽早释放无用目标的引证。

大多数程序员在运用暂时变量的时分,都是让引证变量在退出活动域(scope)后,主动设置为null,暗示废物搜集器来搜集该目标,还必须留意该引证的目标是否被监听,假如有,则要去掉监听器,然后再赋空值。

便是说,关于频频请求内存和释放内存的操作,仍是自己操控一下比较好,可是System.gc()的办法不必定适用,最好运用finallize强制履行或许写自己的finallize办法。

================================================

tomcat

遇到TOMCAT出错:java.lang.OutOfMemoryError:Javaheapspace,所以查了材料,找到了解决办法:

IfJavarunsoutofmemory,thefollowingerroroccurs:

Exceptioninthread”main”java.lang.OutOfMemoryError:Javaheapspace

Javaheapsizecanbeincreasedasfollows:

java-Xms-Xmx

Defaultsare:

java-Xms32m-Xmx128m

假如你用win

/tomcat/bin/catalina.bat加上下面的指令:

setJAVA_OPTS=-Xms32m-Xmx256m

假如你用unix/linux

/tomcat/bin/catalina.sh加上下面的指令:

JAVA_OPTS=”-Xms32m-Xmx256m”

jvm内存检查与剖析东西

业界有许多强大的javaprofile的东西,比方Jporfiler,yourkit,这些收费的东西我就不想说了,想说的是,其实java自己就供给了许多内存监控的小东西,下面列举的东西仅仅一小部分,细心研究下jdk的东西,仍是蛮有意思的呢:)

1:gc日志输出

在jvm发动参数中参加-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimestamps-XX:+PrintGCApplicationStopedTime,jvm将会依照这些参数次序输出gc概要信息,详细信息,gc时刻信息,gc形成的运用暂停时刻。假如在刚才的参数后边参加参数-Xloggc:文件途径,gc信息将会输出到指定的文件中。其他参数还有

-verbose:gc和-XX:+PrintTenuringDistribution等。

2:jconsole

jconsole是jdk自带的一个内存剖析东西,它供给了图形界面。能够检查到被监控的jvm的内存信息,线程信息,类加载信息,MBean信息。

jconsole坐落jdk目录下的bin目录,在windows下是jconsole.exe,在unix和linux下是jconsole.sh,jconsole能够监控本地运用,也能够监控长途运用。要监控本地运用,履行jconsolepid,pid便是运转的java进程id,假如不带上pid参数,则履行jconsole指令后,会看到一个对话框弹出,上面列出了本地的java进程,能够选择一个进行监控。假如要长途监控,则要在长途服务器的jvm参数里参加一些东西,由于jconsole的长途监控基于jmx的,关于jconsole详细用法,请见专门介绍jconsle的文章,我也会在博客里专门详细介绍jconsole。

3:jviusalvm

在JDK6update7之后,jdk推出了别的一个东西:jvisualvm,java可视化虚拟机,它不光供给了jconsole相似的功用,还供给了jvm内存和cpu实时确诊,还有手动dump出jvm内存状况,手动履行gc。

和jconsole一样,运转jviusalvm,在jdk的bin目录下履行jviusalvm,windows下是jviusalvm.exe,linux和unix下是jviusalvm.sh。

4:jmap

jmap是jdk自带的jvm内存剖析的东西,坐落jdk的bin目录。jdk1.6中jmap指令用法:

Usage:

jmap-histo<pid>(toconnecttorunningprocessandprinthistogramofjavaobjectheap

jmap-dump:<dump-options><pid>(toconnecttorunningprocessanddumpjavaheap)

dump-options:format=bbinarydefaultfile=<file>

dumpheapto<file>

Example:jmap-dump:format=b,file=heap.bin<pid>

jmap-histo在屏幕上显现出指定pid的jvm内存状况。以我本机为例,履行该指令,屏幕显现:

1:242062791864<constMethodKlass>

2:223712145216[C

3:242061940648<methodKlass>

4:19511364496<constantPoolKlass>

5:265431282560<symbolKlass>

6:63771081744[B

7:1793909688<constantPoolCacheKlass>

8:1471614624<instanceKlassKlass>

9:14581548336[Ljava.lang.Object;

10:3863513640[I

11:20677496248java.lang.String

12:3621312776[Ljava.util.HashMap$Entry;

13:3335266800java.lang.reflect.Method

14:8256264192java.io.ObjectStreamClass$WeakClassKey

15:7066226112java.util.TreeMap$Entry

16:2355173304[S

17:1687161952java.lang.Class

18:2769150112[[I

19:3563142520java.util.HashMap

20:5562133488java.util.HashMap$Entry

Total23901917140408

为了便利检查,我删掉了一些行。从上面的信息很简略看出,#instance指的是目标数量,#bytes指的是这些目标占用的内存巨细,classname指的是目标类型。

再看jmap的dump选项,这个选项是将jvm的堆中内存信息输出到一个文件中,在我本机履行

jmap-dump:file=c:\dump.txt340

留意340是我本机的java进程pid,dump出来的文件比较大有10几M,并且我仅仅开了tomcat,跑了一个很简略的运用,且没有任何拜访,能够想象,大型繁忙的服务器上,dump出来的文件该有多大。需求知道的是,dump出来的文件信息是很原始的,绝不合适人直接观看,而jmap-histo显现的内容又太简略,例如只显现某些类型的目标占用多大内存,以及这些目标的数量,可是没有更详细的信息,例如这些目标分别是由谁创立的。那这么说,dump出来的文件有什么用呢?当然有用,由于有专门剖析jvm的内存dump文件的东西。

5:jhat

上面说了,有许多东西都能剖析jvm的内存dump文件,jhat便是sunjdk6及以上版别自带的东西,坐落jdk的bin目录,履行jhat-J-Xmx512m[file],file便是dump文件途径。jhat内置一个简略的web服务器,此指令履行后,jhat在指令行里显现剖析成果的拜访地址,能够用-port选项指定端口,详细用法能够履行jhat-heap检查协助信息。拜访指定地址后,就能看到页面上显现的信息,比jmap-histo指令显现的丰厚得多,更为详细。

6:eclipse内存剖析器

上面说了jhat,它能剖析jvm的dump文件,可是全部是文字显现,eclipsememoryanalyzer,是一个eclipse供给用于剖析jvm堆dump的插件,网址为http://www.eclipse.org/mat,它的剖析速度比jhat快,剖析成果是图形界面显现,比jhat的可读性更高。其实jvisualvm也能够剖析dump文件,也是有图形界面显现的。

7:jstat

假如说jmap倾向于剖析jvm内存中目标信息的话,那么jsta便是倾向于剖析jvm内存的gc状况。都是jvm内存剖析东西,但明显,它们是从不同维度来剖析的。jsat常用的参数有许多,如-gc,-gcutil,-gccause,这些选项详细效果可检查jsat协助信息,我经常用-gcutil,这个参数的效果不断的显现当时指定的jvm内存的废物搜集的信息。

我在本机履行jstat-gcutil34010000,这个指令是每个10秒钟输出一次jvm的gc信息,10000指的是间隔时刻为10000毫秒。屏幕上显现如下信息(我只取了第一行,由于是按的必定频率显现,所以实际履行的时分,会有许多行):

S0S1EOPYGCYGCTFGCFGCTGCT

54.620.0042.8743.5286.2417925.093337.67012.763

额。。。怎么说呢,要看懂这些信息代表什么意思,还必须对jvm的gc机制有必定的了解才行啊。其实假如对sun的hotspotjvm的gc比较了解的人,应该很简略看懂这些信息,可是不清楚gc机制的人,有点不可思议,所以在这里我仍是先讲讲sun的jvm的gc机制吧。说到gc,其实不只仅仅仅java的概念,其实在java之前,就有许多言语有gc的概念了,gc嘛便是废物搜集的意思,更多的是一种算法性的东西,而跟详细言语没太大关系,所以关于gc的历史,gc的干流算法我就不讲了,那扯得太远了,扯得太远了便是扯淡。sun现在的jvm,内存的管理模型是分代模型,所以gc当然是分代搜集了。分代是什么意思呢?便是将目标依照生命周期分成三个层次,分别是:新生代,旧生代,耐久代。目标刚开始分配的时分,大部分都在新生代,当新生代gc提交被触发后了,履行一次新生代范围内的gc,这叫minorgc,假如履行了几回minorgc后,还有目标存活,将这些目标转入旧生代,由于这些目标现已通过了组织的重重检测了哇。旧生代的gc频率会更低一些,假如旧生代履行了gc,那便是fullgc,由于不是局部gc,而是全内存范围的gc,这会形成运用中止,由于全内存搜集,必须封闭内存,不许有新的目标分配到内存,耐久代便是一些jvm期间,根本不会消失的目标,例如class的界说,jvm办法区信息,例如静态块。需求首要的是,新生代里又分了三个空间:eden,susvivor0,susvivor1,按字面上来了解,便是伊甸园区,幸存1区,幸存2区。新目标分配在eden区中,eden区满时,采用标记-复制算法,即检查出eden区存活的目标,并将这些目标复制到是s0或s1中,然后清空eden区。jvm的gc说开来,不仅仅这么简略,例如还有串行搜集,并行搜集,并发搜集,还有著名的火车算法,不过那说得太远了,现在对这个有大致了解就好。说到这里,再来看一下上面输出的信息:

S0S1EOPYGCYGCTFGCFGCTGCT

54.620.0042.8743.5286.2417925.093337.67012.763

S0:新生代的susvivor0区,空间运用率为54..62%

S1:新生代的susvivor1区,空间运用率为0.00%(由于还没有履行第二次minor搜集)

E:eden区,空间运用率42.87%

O:旧生代,空间运用率43.52%

P:耐久带,空间运用率86.24%

YGC:minorgc履行次数1792次

YGCT:minorgc消耗的时刻5.093毫秒

FGC:fullgc履行次数33

FGCT:fullgc消耗的时刻7.670毫秒

GCT:gc消耗的总时刻12.763毫秒

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容

图片-小白之家
图片-小白之家