您的位置:首页 > 编程语言 > Java开发

关于java.lang.outofmemoryerror的报错处理

2007-12-14 18:37 441 查看
问题陈述:

tomcat服务器运行一段时间后,tomcat控制台报错:
java.lang.outOfMemoryError,java.lang.outOfMemoryError
然后tomcat服务立即死,只有重启tomcat才能恢复服务

初步确定原因

1,java虚拟机jvm内存过小
2,程序不严密,产生太多的垃圾

解决对策:

针对原因1:
增加jvm的内存大小。
JVM即是java虚拟机,它运行时候占用一定的内存,其大小是有限定的,如果程序在运行时jvm占用的内存大于某个限度,则会产生内存益处,也就是“java.lang.outofmemoryerror”。
作为web容器的tomcat(包括resin等)在运行时候,会直接或间接产生一个java.exe进程,可以看成一个jvm进程。由于这些web容器在不停的运行,也就是jvm不停的工作,jvm会不断的产生垃圾(所胃的垃圾,简单可认为是指程序进行后遗留下的无用的对象),也会不停的回收垃圾。当回收垃圾的速度比产生垃圾的速度的慢时,垃圾就会不停的增长,当垃圾增长超过一定限度,也就是垃圾对象占用的内存超过jvm内存的最大限度。此时jvm就会出现运行时错误,也就是error,由于error属于致命错误,不能处理,故tomcat等就会断掉,只有重启tomcat。
针对此,我们可以设想,如果jvm内存的没有限度,并且有无限大的内存,那jvm就永远不会出现内存溢出了。但是,傻子都知道我们在做白日梦。既然这样行不通,那我们就退一步,所胃退一步海阔天空嘛,我们可以适当增大jvm的最大内存,以缓解jvm来不及回收垃圾而导致的内存不断增长。jdk1.4默认jvm的最大内存为128M,我们可以把tomcat等web容器的jvm最大内存加大,比如我们可以增大到256M,甚至1G等,只要不超过计算的内存都行,不过凡事都有个度,设置过大肯定不是最好的。至于要个jvm设置多大的内存则要通过我们不断尝试。
如何增加jvm的内存呢?
第一,在执行某个class文件时候,可以使用java -Xmx256M aa.class来设置运行aa.class时jvm所允许占用的最大内存为256M。
第二,对tomcat容器,可以在启动时对jvm设置内存限度。对tomcat,可以在catalina.bat中添加:
"set CATALINA_OPTS=-Xms128M -Xmx256M
set JAVA_OPTS=-Xms128M -Xmx256M",或者把%CATALINA_OPTS%和%JAVA_OPTS%代替为-Xms128M -Xmx256M,其具体操作可以到网上去找找。
第三,对resin容器,同样可以在启动时对jvm设置内存限度。在bin文件夹下创建一个startup.bat文件,内容如下:
@echo off
call "httpd.exe" "-Xms128M" "-Xmx256M"
:end
其中"-Xms128M"为最小内存,"-Xmx256M"为最大内存。
第四,其他容器,如ibm webswhere,可以通过配置文件加大jvm内存。
第五,修改jdk内核,个人想法,但没去研究过。

针对原因2:
由于jvm产生的垃圾是由我们所写的代码产生的,质量好的代码产生的垃圾少,相反就会产生很多垃圾。由于jvm的最大内存不能无限增大,故增大jvm的最大内存应该是在代码已经达到很优化时才实施的,所以优化程序才是我们最先要做的。
如何优化程序:
第一,避免死循环。仔细检查程序,防止出现死循环,这是比较容易检查的。
第二,可以适当手动回收垃圾
第三,
应该及时释放种资源:内存, 数据库的各种连接。
释放资源的时候不能依赖于java的垃圾自动回收机制,最好也不要用finalize方法,因为无用单元回收不是一个完全可以确定的过程,作为低优先级进程,往往是系统没有内存时才调用垃圾回收进程。

由于我们在Java程序中声明了好多对象,占用了内存空间,程序结束时没有将这些对象或对象的引用进行释放,从而导致Java虚拟机(JVM)进行垃圾回收(GC)时,不能够回收这些对象。这样,Java所用的内存就会一直增加,直至溢出,进而导致Resin死机。
导致Java内存溢出的根本原因是Java程序的不规范或不健壮。因此,从根本上解决Java内存溢出的唯一方法就是修改Java程序,及时地释放没用的对象,释放内存空间。
除了这个方法以外,还有一些应急措施,可以临时缓解一下系统的运行。Resin默认情况是死机以后不能访问网站,必须手动重启Resin,但不可能一直看在机器旁边,看Resin有没有死机。所以这里介绍一种让Resin自重启的方法。
打开Resin的配置文件resin.conf(一般情况下,该文件在Resin目录的conf文件夹下)。里面有一段内容如下所示:

这段内容默认情况下是被注释的。它的功能是让Resin每隔一分钟就测试一下能否访问/ping/ping.jsp文件,测试时间是1s,如果不成功,就重试三次。如果三次都不成功,Resin就自动重启。所以将这段话下半部分(从"")的注释去掉,然后在Resin的发布目录中新建一个文件夹ping,在ping文件夹下新建一个ping.jsp文件,文件中可以写入简单的一句话,如:。
好,大功告成,启动Resin,这样就不用担心Java内存溢出导致Resin死机了,因为Resin死机后会马上重新启动。
当然,这只是应急措施,不是长久之计。如果从长计议,还是要耐心的更改Java程序!!!

有人说:
“不断的将被选中的字符串加到某一字符串末尾,当长度超过一定量是就提示:
java.lang.StringIndexOutOfBoundsException: String index out of range: 10
”说明String有长度限制。
看一下Java API就会知道
java.lang.StringIndexOutOfBoundsException出现的情况是
Thrown by String methods to indicate that an index is either negative or greater than the size of the string. For some methods such as the charAt method。
上面的错误是因为
String.length()<10;
而你又要取index>=10的字符,自然就会抛出上面的例外。
String其实是没有限制的,而是当String太大了,超过JVM的自身的内存后会抛出
java.lang.OutOfMemoryError错误

下面作个实验:
public class testString{
public static void main(String args[])
{
String s="abbbbb";
System.out.println("JVM MAX MEMORY: "+Runtime.getRuntime().maxMemory()/1024/1024+"M");
System.out.println("JVM IS USING MEMORY:"+Runtime.getRuntime().totalMemory()/1024/1024+"M");
Runtime.getRuntime().traceMethodCalls(true);
while(true)
{
try{
s=s+s;

}catch(Exception e)
{
System.out.println(e);
}
catch(Error o)
{ String unit = null;
int sizeb = s.length();
int size = sizeb;
int time = 0;
while(size>1024)
{
size = size/1024;
time++;
}
switch(time)
{
case 0: unit = "byte";break;
case 1: unit = "k"; break;
case 2: unit = "M"; break;
default : unit = "byte";
}

System.out.println("String has used memory:"+size+unit);
System.out.println("JVM IS USING MEMORY:"+(float)Runtime.getRuntime().totalMemory()/1024/1024+"M");
System.out.println("MemoryError:"+o);
break;
}

}
}
}
然后我们用JVM的默认参数执行(我的机器内存是128M)
java testString
结果:
JVM MAX MEMORY: 128M
JVM IS USING MEMORY:1M
String has used memory:12M
JVM IS USING MEMORY:63.5625M
MemoryError:java.lang.OutOfMemoryError
开始JVM使用的内存是1M,当String为12M,JVM使用了63M多时
JVM溢出。

然后,我们用限制JVM内存大小的参数来执行,限制最大内存5M
java -mx5m testString
结果:
JVM MAX MEMORY: 70M
JVM IS USING MEMORY:1M
String has used memory:768.0k
JVM IS USING MEMORY:5.9375M
MemoryError:java.lang.OutOfMemoryError
开始JVM使用的内存是1M,当String为768k,JVM使用了5M多时
JVM溢出。

大家还可以改变 -mx参数,来进一步做实验。
以上两个实验证明,String是没有长度限制的,而是有JVM的内存限制了String的长度。同时说明,并不会抛出任何Exception而只会抛出Error.

OutMemoryError表明程序的设计很差,或者遇到了超出编程人员所预想的大批量的数据。不管哪种情况,都只有下面这几种解决办法。它们是:

设计人员重新设计程序,不致使程序一次载入所有的数据。

数据可以分割成更小的块。

可以为程序分配更多的内存。

为Java虚拟机提供更多的内存。

而上面的例子是为虚拟机提供更多的内存

=======================================
其实应该少用String这东西,特别是 String的 +=操作
不仅原来的String对象不能继续使用,主要是又要new出N多的新对象出来,再多的memory也要out~~
String用char array实现,就肯定由长度限制的,不能用memory来衡量

==================================
例如上面的程序改用StringBuffer实现,就可以得到极大的改善。
下面是我改用StringBuffer做的测试:
注意:程序循环了2097150次!
是使用String的程序的99864倍!

public class TestStringBuffer{
public static void main(String args[])
{
String s="abbbbb";
StringBuffer sb = new StringBuffer(s);
System.out.println("JVM IS USING MEMORY:"+
(Runtime.getRuntime().totalMemory()/1024/1024)+
"M");
Runtime.getRuntime().traceMethodCalls(true);

int count = 0;
while(true)
{
try{
sb.append(s);
count++;

}catch(Exception e)
{
System.out.println(e);
}
catch(Error o)
{
String unit = null;
int size = sb.length();
size *= 2;

int time = 0;
while(size>1024)
{
size = size/1024;
time++;
}
switch(time)
{
case 0: unit = "byte";break;
case 1: unit = "k"; break;
case 2: unit = "M"; break;
default : unit = "byte";
}

System.out.println("Loop times:"+count);
System.out.println("String has used memory:"+size+unit);
System.out.println("JVM IS USING MEMORY:"+
(float)Runtime.getRuntime().totalMemory()/1024/1024+
"M");
System.out.println("MemoryError:"+o);
break;
}

}
}
}

输出结果:
JVM IS USING MEMORY:1M
Loop times:2097150
String has used memory:23M
JVM IS USING MEMORY:63.75M
MemoryError:java.lang.OutOfMemoryError

=====================
从另一方面说,如果你要处理的字符串达到百兆甚至上GB,使用String对象,根本没法工作,所以这个问题不需要太多讨论。看一下jdk的源文件,String的长度是String对象的一个成员count,类型是int,不是long,也不是char。知道这些,我认为够了。

如何设置Tomcat的JVM虚拟机内存大小

Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个java虚拟机。您可以选择自己的需要选择不同的操作系统和对应的JDK的版本(只要是符合Sun发布的Java规范的),但我们推荐您使用Sun公司发布的JDK。确保您所使用的版本是最新的,因为Sun公司和其它一些公司一直在为提高性能而对java虚拟机做一些升级改进。一些报告显示JDK1.4在性能上比JDK1.3提高了将近10%到20%。

可以给Java虚拟机设置使用的内存,但是如果你的选择不对的话,虚拟机不会补偿。可通过命令行的方式改变虚拟机使用内存的大小。如下表所示有两个参数用来设置虚拟机使用内存的大小。

参数

描述

-Xms

JVM初始化堆的大小

-Xmx

JVM堆的最大值

这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。

Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。

Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh的前面,增加如下设置:

JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】'

需要把这个两个参数值调大。例如:

JAVA_OPTS='-Xms256m -Xmx512m'

表示初始化内存为256MB,可以使用的最大内存为512MB。

另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。如果你把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。

如果系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。如果垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的详细输出,研究 垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的 80% 作为堆大小。当增加处理器时,记得增加内存,因为分配可以并行进行,而垃圾收集不是并行的。

Tomcat 5常用优化和配置

1、JDK内存优化:

Tomcat默认可以使用的内存为128MB,Windows下,在文件{tomcat_home}/bin/catalina.bat,Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,增加如下设置:

JAVA_OPTS='-Xms[初始化内存大小] -Xmx[可以使用的最大内存]

一般说来,你应该使用物理内存的 80% 作为堆大小。

2、连接器优化:

在tomcat配置文件server.xml中的配置中,和连接数相关的参数有:

maxThreads:

Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。默认值200。

acceptCount:

指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。

minSpareThreads:

Tomcat初始化时创建的线程数。默认值4。

maxSpareThreads:

一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值50。

enableLookups:

是否反查域名,默认值为true。为了提高处理能力,应设置为false

connnectionTimeout:

网络连接超时,默认值60000,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

maxKeepAliveRequests:

保持请求数量,默认值100。

bufferSize:

输入流缓冲大小,默认值2048 bytes。

compression:

压缩传输,取值on/off/force,默认值off。

其中和最大连接数相关的参数为maxThreads和acceptCount。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。

3、tomcat中如何禁止和允许列目录下的文件

在{tomcat_home}/conf/web.xml中,把listings参数设置成false即可,如下:

<servlet>
...
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
...
</servlet>
4、tomcat中如何禁止和允许主机或IP地址访问
<Host name="localhost" ...>
...
<Valve className="org.apache.catalina.valves.RemoteHostValve"
allow="*.mycompany.com,www.yourcompany.com"/>
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
deny="192.168.1.*"/>
...
</Host>

这是我们服务器的配置,具体文件不能给你,因为涉及到公司机密,所以那一行我给你,我拷贝下来了

JAVA_OPTS='-server -Xms512m -Xmx768m -XX:NewSize=128m -XX:MaxNewSize=192m -XX:SurvivorRatio=8'

2007-6-19 23:38 51video
TOMCAT内存和连接数配置(转)

如果是使用的catalina.sh(linux)或Catalina.bat(win)启动的:
修改这两个文件,加上下面这句:
SET CATALINA_OPTS= -Xms64m -Xmx128m

如果使用的winnt服务启动:
打开C:/WINNT/system32/regedt32.exe,在HKEY_LOCAL_MACHINE-->SOFTWARE-->Apache Software Foundation-->Process Runner 1.0-->Tomcat5-->Parameters
修改属性:
-Xms64m
-Xmx128m

有人建议Xms和Xmx的值取成一样比较好,说是可以加快内存回收速度。

加大tomcat连接数:

在tomcat配置文件server.xml中的配置中,和连接数相关的参数有:
minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为10
maxProcessors:最大连接线程数,即:并发处理的最大请求数,默认值为75
acceptCount:允许的最大连接数,应大于等于maxProcessors,默认值为100
enableLookups:是否反查域名,取值为:true或false。为了提高处理能力,应设置为false
connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

其中和最大连接数相关的参数为maxProcessors和acceptCount。如果要加大并发连接数,应同时加大这两个参数。

web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: