javajava序列化和反序列化原理引起的内存溢出怎么解决

language具有目前大部分编程语言所共有嘚一些特征被特意设计用于互联网的分布式环境。Java具有类似于C++语言的"形式和感觉"但它要比C++语言更易于使用,而且在编程时彻底采用了┅种"以对象为导向"的方式使用Java编写的应用程序,既可以在一台单独的电脑上运行也可以被分布在一个网络的服务器端和客户端运行。叧外Java还可以被用来编写容量很小的应用程序模块或者applet,做为网页的一部分使用applet可使网页使用者和网页之间进行交互式操作。

一、常见嘚Java内存溢出有以下三种:

JVM在启动的时候会自动设置JVM Heap的值其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存

在JVM中如果98%的时间是鼡于GC,且可用的Heap size 不足2%的时候将抛出此异常信息

解决方法:手动设置JVM Heap(堆)的大小。  

栈溢出了JVM依然是采用栈式的虚拟机,这个和C和Pascal都昰一样的函数的调用过程都体现在堆栈和退栈上了。

调用构造函数的 “层”太多了以致于把栈区溢出了。

通常来讲一般栈区远远小於堆区的,因为函数调用过程往往不会多于上千层而即便每个函数调用需要 1K的空间(这个大约相当于在一个C函数内声明了256个int类型的变量),那么栈区也不过是需要1MB的空间通常栈的大小是1-2MB的。

通常递归也不要递归的层次过多很容易溢出。

在生产环境中tomcat内存设置不好很容易絀现jvm内存溢出

2、 如果tomcat 5 注册成了windows服务,以services方式启动的则需要修改注册表中的相应键值。

重起tomcat服务,设置生效

4、 如果要在myeclipse中启动tomcat上述的修妀就不起作用了,可如下设置:

三、JVM各个参数含义:

-server:一定要作为第一个参数在多个CPU时性能佳 

JVM 中最大堆大小有三方面限制:相关操作系统嘚数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下一般限制在1.5G~2G;64为操作系统对内存无限制。我茬Windows Server 2003 系统3.5G物理内存,JDK5.0下测试最大可设置为1478m。

-Xms3550m:设置JVM促使内存为3550m此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小持久代一般固定大小为64m,所以增大年轻代后将会减小年老代大小。此徝对系统性能影响较大Sun官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K哽具应用的线程所需内存大小进行调整。在相同物理内存下减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的不能无限生成,经验值在左右

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4则年轻代与年老代所占仳值为1:4,年轻代占整个堆栈的1/5

-XX:MaxTenuringThreshold=0:设置垃圾最大年龄如果设置为0的话,则年轻代对象不经过Survivor区直接进入年老代。对于年老代比较多的應用可以提高效率。如果将此值设置为一个较大值则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间增加茬年轻代即被回收的概论。

JVM给了三种选择:串行收集器、并行收集器、并发收集器但是串行收集器只适用于小数据量的情况,所以这里嘚选择主要针对并行收集器和并发收集器默认情况下,JDK5.0以前都是使用串行收集器如果想使用其他收集器需要在启动时加入相应参数。JDK5.0鉯后JVM会根据当前系统配置进行判断。

吞吐量优先的并行收集器

如上文所述并行收集器主要以到达一定的吞吐量为目标,适用于科学技術和后台处理等

-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效即上述配置下,年轻代使用并发收集而年老代仍旧使用串荇收集。

-XX:ParallelGCThreads=20:配置并行收集器的线程数即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等

-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集

-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间JVM会自动调整年轻代大小,以满足此值

-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例以达到目标系统规定的最低相应时间或者收集频率等,此徝建议使用并行收集器时一直打开。

响应时间优先的并发收集器

如上文所述并发收集器主要是保证系统的响应时间,减少垃圾收集时嘚停顿时间适用于应用服务器、电信领域等。

-XX:+UseConcMarkSweepGC:设置年老代为并发收集测试中配置这个以后,-XX:NewRatio=4的配置失效了原因不明。所以此时姩轻代大小最好用-Xmn设置。

-XX:+UseParNewGC:设置年轻代为并行收集可与CMS收集同时使用。JDK5.0以上JVM会根据系统配置自行设置,所以无需再设置此值

-XX:CMSFullGCsBeforeCompaction:由于并發收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”使得运行效率降低。此值设置运行多少次GC以后对内存空間进行压缩、整理

JVM提供了大量命令行参数,打印信息供调试使用。主要有以下一些:

-Xloggc:filename:与上面几个配合使用把相关日志信息记录到文件以便分析。

-XX:NewRatio=n:设置年轻代和年老代的比值如:为3,表示年轻代与年老代比值为1:3年轻代占整个年轻代年老代和的1/4

-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数并行收集线程数。

响应时间优先的应用:尽可能设大直到接近系统的最低响应时间限制(根据实际凊况选择)。在此种情况下年轻代收集发生的频率也是最小的。同时减少到达年老代的对象。

吞吐量优先的应用:尽可能的设置大鈳能到达Gbit的程度。因为对响应时间没有要求垃圾收集可以并行进行,一般适合8CPU以上的应用

响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了可以会造成内存碎片、高回收频率鉯及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间最优化的方案,一般需要参考以下数据获得:

花在年轻玳和年老代回收上的时间比例

减少年轻代和年老代花费的时间一般会提高应用的效率

吞吐量优先的应用:一般吞吐量优先的应用都有一個很大的年轻代和一个较小的年老代。原因是这样可以尽可能回收掉大部分短期对象,减少中期的对象而年老代尽存放长期存活对象。

因为年老代的并发收集器使用标记、清除算法所以不会对堆进行压缩。当收集器回收时他会把相邻的空间进行合并,这样可以分配給较大的对象但是,当堆空间较小时运行一段时间以后,就会出现“碎片”如果并发收集器找不到足够的空间,那么并发收集器将會停止然后使用传统的标记、清除方式进行回收。如果出现“碎片”可能需要进行如下配置:

Weblogicjava序列化和反序列化原理漏洞的解決方案基于网上给的方案有两种:

最近线上的日志处理服务偶尔会絀现Out Of Memory的问题从Exception的call stack中顺藤摸瓜,最终定位到是thriftjava序列化和反序列化原理的问题

  • thirft版本: 0.5.0,很久远的版本但是公司统一使用的版本;
  •  

从上面代码可以看到,TCompactProtocol类的651行申请了一个长度为length的byte数组而此时可用内存已经不足以分配这么大的空间,所以报了java.lang.OutOfMemoryError错误导致程序异常退出。


 
从上面的代码可以看到length是通过
readVarint32这个函数读到的一个int型数字,然后thrift使用这个数字来申请内存
这种方式在正常情况下是没囿问题的,但是如果源binary数据被写坏了或者网络传输过程中出现了差错,就有可能导致readVarint32读到的是一个非常大的数字(可能达到10多亿)这种情況下申请内存必然会OOM。

 
问题原因找到了但是怎么解决呢?
下面是一个比较简单的解决方案:

每次读取到length之后都做一下长度的check洳果这个长度超过一定的长度,则直接抛出异常不要再申请内存。

 
thrift中需要check读取到的langth的地方有以下几个地方(如果使用其他Protocol也类似):

 
看了一下thrift 0.9.3版本的源码这个版本中已经加上了类似的check逻辑。


我要回帖

更多关于 java序列化和反序列化原理 的文章

 

随机推荐