HeapSize OOM


public static void main(String[] args) {
List<String> list = Lists.newArrayList();
while (true) {
list.add("hello");
}
}

会抛如下异常

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:213)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:187)
at java.util.ArrayList.add(ArrayList.java:411)
at oscar.test.oom.TOom.main(TOom.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

这是java堆空间的溢出,也就是说Old区剩余的内存,已经无法满足要晋升到Old区对象的大小了。

OOM原因


代码问题:

一般内存泄露不会这么显而易见。

可能有很多对象有较长的生命周期,或者在全局区域增加一条数据导致隐藏的数据膨胀,或者死循环写入数据。这种时候就需要改代码。

其他问题:

由于并发导致内存无法被GC,或者说很多对象还有引用,即对象所在声明周期内的代码还没有执行完。这时候想到的方法就是“提速”。代码提速了,相同的对象声明周期会更加短暂,会很快被当做垃圾回收。

强制赋值null:

如果一个占据很大内存的对象,在后续的声明周期中没有用途,但后续声明周期依然很长(比如后面会做一些IO操作),这时可以用Object=null来帮助GC。

不提倡大部分代码都这样写!当方法脱离作用域后,相应的局部变量会自动被注销掉,只有它引用的对象后续有较长的声明周期,且对象占用空间较大时(达到KB级别),才有必要这样做,否则代码很不干净。

public void method() {

List<String> list = getList();//该方法返回一个超级大的list

list = null;//后面对list没有任何操作了,而且后面有个操作时间很长,这时候帮助gc

// do something

Thread.sleep(10000);

// 如果不帮助gc,当该方法脱离作用域后,该list局部变量才会被自动注销,多存在了10秒钟

}

配置:

当代码无法优化,程序跑的很快时候还发生OOM,这时就考虑改配置。最需要改的是堆的大小,将堆的空间设置的最后大,来承载更多时间单位内并发所用到的内存区域。

这个需要一个平衡,很大的内存不容易GC,但是如果很大的内存发生GC会很恐怖。

不停GC,GC很慢


这个时候的内存泄露很难发现。

因为绝大多数对象是活着的,GC过程标记活着的对象很耗时。对压缩环节来说,因为存在大量存活对象,空隙变得很小,压缩时间会变长。

这个时候系统表现出来的是,不停做FullGC,每次GC释放一点点内存,马上又满了,不断反复。当次数达到一定量,并且平均FullGC时间达到一定比例,会报错:OutOfMemory:GC over head limite exceed。

Sun 官方对此的定义是:"并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作。"

这个时候不是真正的OOM,而是提前抛出异常,防止OOM发生。

举例:Session

Tomcat的session问题。程序中将一个session保存的一个全局的ConcurrentHashMap。由于外部有大量的HttpClient访问创建的临时session,这些httpclient程序在访问时并没有保存sessionKey和cookie,导致每次请求都以为以前没有创建过session,都会重新创建session。session的创建时发生在request.getSession()方法调用时,这些操作在一些框架中也会隐藏创建,虽然这些session很小,但是积少成多,积土成山。

OOM的后果


OOM不一定会宕机。大多OOM都是静态变量指向了一个只增不减的对象,比如一个HashMap。但有时候OOM不一定是这么来的。

如果一个局部变量引发的OOM,可以try catch住,而不是抛到容器顶层来处理,这样可以让进程继续存在。当抛出的错误在方法外捕获的时候,局部变量的作用域其实已经脱离,如果发生一次FullGC,内存可以释放掉,系统可以照常运行,比如:

public static void testOOM() {
try {
List<String> list = Lists.newArrayList();
while (true) {
list.add("hello");
}
} catch (Throwable e) {
System.out.println("message:" + e.getMessage());
}
}

但是这样的话,不容易发现问题,只有这段代码引发的错误被大家知道,才会去跟进,不如OOM宕机能够更加直接的让人认识到问题的严重性。

而且,这种情况的OOM,用jmap将内存dump出来的时候,或者内存溢出时dump出来看到的内存情况可能很正常,因为在dump的时候会先做一次FullGC,这个时候局域变量脱离了作用域,就没办法捕获它了。

PermGen OOM


永久代存放的东西有Class和一些常量。例如:

public static void testGenOOM() {
List<String> list = Lists.newArrayList();
int i = 0;
while (true) {
list.add(("hello1234567890-qwertyuioasdfghjkl;zxcvbnm,.!@#$%^&*()" + (i++)).intern());
}
}

这样就会导致Perm的OOM,OutOfMemoryError,PermGen space。

如果把List去掉,只是不停的intern(),则不会OOM。但是并不是说这样就没有问题了,因为这时候系统在不停的做FullGC(用-XX:PrintGCDetail就可以看到)。FullGC会回收常量池中的内容,这也是FullGC的原因之一。

JDK1.7的String常量已经不放在PermGen区了。

Class的加载也可能导致PermGen的OOM。注意这里Class并没有被卸载,而是导致内存溢出。

因为Class的卸载条件非常苛刻,只有这个Class所对应的的ClassLoader下的所有Class(包括其它类的Class)都没有活着的对象引用,才会被卸载。

如果每次CGlib动态创建时,都重新给它设置一个ClassLoader,这个ClassLoader只加载这个类的话,且加载之后这个类很快就没有引用了,这样就不会OOM了。

所以:如果想要动态加载一些类,或者动态编译一段java代码,最好有一个单独的ClassLoader,这样如果Class发生变化或者被替换,原先的Class就可以被当做垃圾释放了。

DirectBuffer OOM


java的普通IO采用输入输出流来实现。

输入流:终端->直接内存->JVM。输出流:JVM->直接内存->终端。

这期间有多次Kernel与JVM之间的内存拷贝。有时为了提高速度,会想办法利用直接内存。

Java中有一块区域叫做DirectBuffer,它不是Java Heap的一部分,而是C Heap的一部分。它有大小限制,在FullGC的时候会回收,该区域使用不当会有“大坑”。

StackOverFlow Error


程序运行过程中,方法分配时会分配栈帧(Frame)来存放本地变量,后进先出栈,PC寄存器等信息,如果方法嵌套或者递归调用,在不断分派过程中会占用十分多的空间。

Java为了控制线程栈无休止的增长(防止死递归的出现),就会设置一个私有栈空间大小,量级大概是256KB~1MB。线程私有栈所占用的不再是堆内存,通常叫做Native Memory。若使用空间超过限制,就会出现StackOverFlowError。

如果需要使用递归解决问题,一定要设置好递归调用层数,或者设置好递归结束的条件。

死递归与死循环不同。死循环是类似于while(true),不会造成栈空间的递增。而死递归需要记录退回的路径,就必须记住递推过程中的方法调用过程,以及每个方法运行过程中的本地变量,我们称之为上下文信息。

随着内容增加,就会占用很多内存空间,防止其无限制增长,做了安全处理。

举例:

子类调用父类方法(为了复用),父类再直接或者间接调用子类方法(多态),因为某种因素形成了一个环,变成了死递归。

定位StackOverFlow很容易,线程栈信息明确给出了调用路径。

其他内存溢出现象


unnable to create new native thread

栈本身是占用空间的,只是它占用的是native memory。每个私有栈空间都有大小限制,每一个线程对应它自己的栈空间。如果反复申请了大量线程并让它们处于运行状态,那么这些线程所占用的native memory空间就会很多。

如果物理内存不够,或者操作系统限制了单个进程使用的最大内存,那么当有“大量”线程分配的时候,可能会抛出异常:unnable to create new native thread。它表示无法分配本地内存。

request {} byte for {} out of swap

地址空间开始不够用了。不一定是物理地址,还有swap,显卡,网卡等等。

IOException:too many open files

有太多没有关闭的文件或者套接字。

还有一些其他的比如JNI调用问题,Swap与内存频繁交互导致系统假死,JVM内核bug导致进程crash掉(此时还需要看crash日志)。

遇到OOM时,可以设定HeapDumpOnOutOfMemoryError参数,设定参数之后,JVM会在OOM时dump出一份内存的二进制文件,该文件可以用MAT工具来分析。

java特种兵读书笔记(3-5)——java程序员的OS之OOM相关推荐

  1. Android群英传神兵利器读书笔记——第一章:程序员小窝——搭建高效的开发环境

    Android群英传神兵利器读书笔记--第一章:程序员小窝--搭建高效的开发环境 目录 1.1 搭建高效的开发环境之操作系统 1.2 搭建开发环境之高效配置 基本环境配置 基本开发工具 1.3 搭建程 ...

  2. 读书笔记之《高效程序员的45个习惯----敏捷开发之道》 摘录

    读书笔记之<高效程序员的45个习惯----敏捷开发之道>摘录 此次原创的意思是指这个文章中的内容是由笔者从<高效程序员的45个习惯----敏捷开发之道>书中摘录,而不是别人摘录 ...

  3. Core Java 8 读书笔记-Networking编程

    Core Java 8 读书笔记-Networking编程 作者:老九-技术大黍 原文:Core Java 8th Edition 社交:知乎 公众号:老九学堂(新人有惊喜) 特别声明:原创不易,未经 ...

  4. java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略

    java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...

  5. Java快车读书笔记

    办公自动化:OA 客户关系管理:CRM 人力资源:HR 企业资源计划:ERP 知识管理:KM 供应链管理:SCM 企业设备管理系统:EAM 产品生命周期管理:PLM 面向服务体系架构:SOA 商业智能 ...

  6. java双引号的转义字符_好程序员Java教程分享常见的转义字符

    原标题:好程序员Java教程分享常见的转义字符 好程序员Java教程分享常见的转义字符,在Java字符常量中,反斜杠(\)是一个特殊的字符,被称为转义字符,它的作用是用来转义后面一个字符.转义后的字符 ...

  7. 计算机系统导论第九章,计算机系统导论 -- 读书笔记 -- 第三章 程序的机器级表示 (持续更新)...

    计算机系统导论 -- 读书笔记 -- 第三章 程序的机器级表示 (持续更新) 第三章 程序的机器级表示 3.1 历史观点 3.2 程序编码 1. 命令行 (1)编译 Linux> gcc -Og ...

  8. java实现心形图案|桃心_程序员的浪漫--java打印心形图案

    原标题:程序员的浪漫--java打印心形图案 谁说程序员不能浪漫 单纯的心 双心 加些点缀 代码所用方程: 心:((0.05*x)^2 + (0.1*y)^2-1)^3-(0.05*x)^2 * (0 ...

  9. 精通C语言Java怎么样_作为一个优秀的程序员,真的需要精通C语言吗?

    从事嵌入式开发多年,使用C语言开发已经超过十个年头,除了C语言还用C++,java,python做过项目,程序员的优秀编程语言仅仅占据一部分,而且也不会只是针对C语言,现在很多的编程高手也不懂的C语言 ...

最新文章

  1. 学习IT技术你需要的是书?视频教程?还是老师?
  2. 《深入理解java虚拟机》笔记1——Java内存区域与Java对象
  3. 1.MVC的工作流程
  4. PostgreSQL数据库远程连接功能的开启
  5. Visual studio docker build no such file or directory
  6. 求最近点对算法分析 closest pair algorithm
  7. ahp层次分析法matlab代码_(案例)AHP层次决策分析Matlab编码计算
  8. python俄罗斯方块代码34行_Python:游戏:300行代码实现俄罗斯方块
  9. DRM-X 4.0加密保护与Widevine DRM平台的区别
  10. 基于vue的选择月日组件
  11. 在C ++中将二进制转换为十进制
  12. 你认为的CISP这个证书是怎么样的
  13. owlBus 的uwp版本上架了
  14. 怎样快速实现整片文档中英互译
  15. 组态王网页服务器,组态王6.55WEB全新发布详细步骤
  16. 【探花交友DAY 12 完结】推荐系统
  17. win10网络显示依赖服务器无法启动,Win10专业版遇到“依赖服务或组无法启动”怎么办?...
  18. 电脑被攻击的文件该如何恢复
  19. Unity 制作翻书电子书,外部异步加载千张图片(二)
  20. R拓展包的下载与安装

热门文章

  1. linux上安装python、igraph源码包_linux ubuntu 安装/卸载/删除python-igraph方法
  2. UVA10529 Dumb Bones
  3. 第三章 坐标系与投影转换之基准面和坐标系的分类基本知识
  4. C语言版,传教士与野人渡河问题,使用深度优先搜索法求解(DFS),变态版,随便输入人数和船的最大载人数,人工智能经典题目,简单易懂,注释到位,没有bug
  5. Linux学习笔记---阿里云
  6. 《树莓派开发笔记 - 第1部分 基础篇》第7章 树莓派变身路由器
  7. 计算机科班Java开发好书推荐
  8. STM32 IO口模拟ISO7816(PSAM卡)协议
  9. 计算2个时间段的重叠天数
  10. keil编译后报错Error:L6218E的解决方法