JVM
堆、栈区、本地内存
JVM内存
线程共享区
JAVA堆 方法区,如果耗尽会抛出OOM异常Out of MemoryError
线程私有区
随线程死亡而消亡,在编译时确定所需内存大小
虚拟机栈、本地方法栈、程序计数器
多线程下抛出OOM,单线程抛出SstackOutflowError
直接内存:NIO会频繁使用该区域,直接内存大小不受java堆大小限制,但受本机总内存的限制。可通过MaxDirectMemorySize来设置,默认与堆内存最大值一样。所以也会抛出OOM异常
堆和栈的区别是什么?
1.栈的大小远小于堆内存
2.通过-Xss设置栈内存的大小。-Xms设置堆的开始时大小。 -Xmx设置堆的最大值
JDK 1.6 1.7 1.8的区别
1.6 运行时常量在方法区
1.7 运行时常量在堆,有方法区
1.8 没有方法区,运行时常量在堆,多了元空间,移入本地内存空间
为什么去除方法区
1.永久代存储类、常量、静态变量很容易遇到内存溢出问题,JDK8将类的元数据放入native memory,将字符串池和类的静态变量放入java堆,这样可以通过MaxMetaspaceSize来对元数据区大小进行调整,降低了出现永久代内存溢出的问题
2.对永久代调优的难度很大,JDK8采用此方法将元空间与堆的GC进行隔离。避免永久代引发的Full GC和OOM等问题。
JVM常用的内存参数
JAVA内存溢出
1.OOM:堆、栈、方法区、元数据区、直接内存中数据达到最大容量时产生
2.StackOverFlowError:如果线程请求的栈深度大于虚拟机锁运行的最大深度抛出该异常,其本质还是数据达到最大容量
堆溢出
产生原因
堆用于存储实例的对象,不断的创建对象,且保持应用,避免GC回收,就会到达产生OOM异常
java.lang.OutOfMemoryError:Java heap space
解决方法
使用-XX:+HeapDumpOnOutOfMemoryError可以让java虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析,主要分析哪些对象占用了内存。亦可以用jmap将快照导出。检查哪些对象占用空间比较大,由此判断代码问题,没有问题的考虑调整堆参数
栈溢出
产生原因
线程请求的栈深度大于虚拟机锁允许的最大深度,抛出StackOverFlowError
虚拟机在扩展栈时无法申请到足够的内存空间,抛出OOM
解决办法
StackOverFlowError,通常是调用层级过多,死递归、死循环
OOM,多线程情况下,调小堆和栈容量来换取更多线程支持。另外增大物理内存;或者采用限流,负载均衡的方式,把压力降低。
方法区或元数据区溢出
产生原因
jdk1.6之前,运行时常量池还是方法区的一部分,当常量池满了之后抛出OOM
方法区和元数据区存放的是class相关的信息,如类名、访问修饰符、常量池、方法、静态变量;如果工程中的类比较多,而方法区或者元数据区太小,在启动的时候,容易抛出OOM
解决方案
1.7之前通过 -XX:PerSize,-XX:MaxPerSize,调整大小
1.8之后通过-XX:MetaspaceSize,-XX:MaxMetaspaceSize调整元数据区大小
直接内存溢出
产生原因
通常直接内存溢出的原因是NIO,直接内存溢出最大的特点是HeapDump文件看不出明显异常。
解决办法
直接内存大小不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认值与堆内存最大值一样)
垃圾回收
主要是回收堆内存,基于分带的思想
堆内存:新生代(1/3堆空间)和老年代(2/3堆空间);新生代Eden:FROM:TO=8:1:1