jvm各个区域的OOM和

java堆、方法区还有直接内存都可能会出现OOM。
java虚拟机栈和本地方法栈是线程独有的会出现SOF和OOM。
java虚拟机栈存放的是java方法的执行过程里面需要的变量、引用、方法出口等信息。

  • 当栈深度不够而且又无法申请到新的内存来扩展栈的时候就会OOM。
  • 当线程请求的栈深度大于虚拟机所允许的最大深度的时候就会SOF。
  • 当线程请求创建栈的时候内存不够分配那么就会OOM。这里的OOM应该怎么处理,由于没有多余的空间分配给栈了,我们可以想是不是因为给每个线程分配的stack空间太大了,然后导致栈空间不够用导致的;32操作系统内存限定为2g那么,通过参数指定了java堆和方法区的最大值,程序计数器只占用很少的空间,虚拟机本身的内存也不算在内的话剩下的空间就由java虚拟机栈和本地方法栈来共享了,所以每个线程分配的stack越大那么可创建的线程就越少,如果因为创建线程导致了OOM,如果不能减少线程那么可以换成64位机器,如果不能更换64位机器那么只能通过减少java堆区和方法区的大小来增加线程的容量了。

java堆存放的是java对象,是线程共享的区域。

  • 当创建新对象并且堆内存不够去申请内存也申请不下来的时候就会OOM,并且提示java heap space。但是要分析是内存溢出还是内存泄漏,通过Eclipse Memory Analyzer分析如果是内存泄漏,那么可以通过工具查看泄露对象的GC Roots引用链。于是就能找到泄露对象是通过怎样的路径和GC Roots关联的从而导致GC无法回收它,找到泄露对象的类型信息以及GC Roots的引用链就能准确的定位出泄露代码的位置。

方法区存放的是类信息、常量池、静态变量、即时编译后的代码。运行时常量池在方法区。

  • 当运行时常量池需要扩展的时候又申请不到足够的空间那么就会OOM并且提示PermGen space(方法区异常)。
  • 方法区存放类的信息、如类名、访问修饰符、常量池、字段描述、方法描述等,让这个区域OOM的方法就是用大量的类去填满方法区,可以借助Java SE Api(如反射时的GeneratedConstructorAccessor和动态代理等)动态生成类,也可以借助CgLib直接操作字节码运行时生成类;因为使用到cglib的常用框架很多,比如spring和hibernate对类增强时都使用了cglib这类字节码增强技术,因此我们项目中增强的类越多,所需要的方法区就越大。用来保证动态生成的class文件可以载入内存。

本机直接内存溢出:
DirectMemory容量可以通过-XX:MaxDirectMemory指定,如果不指定默认和java堆的最大值(-Xmx)一样。我们可以越过DIrectByteBuffer类,直接通过反射获取Unsafe实例来进行内存分配(Unsafe类的getUnsaf()方法限制了只有引导类加载器才返回实例,也就是设计者希望只有rt.jar中的类才会使用到Unsafe的功能)。因为使用DirectByteBuffer类虽然也会抛出OOM,但是它抛异常的时候并没有真正的向操作系统申请分配内存,而是通过计算得知内存不够然后手动抛出异常;真正分配内存的方法是unsafe.allocateMemory()。

总结:

  • 栈内存、堆内存、方法区(字节码增强区)、运行时常量池(方法区)、直接内存都会OOME。
  • 虽然java的GC机制是自动的,但是我们还是要谨慎编程,因为OOM离我们并不远。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值