1:JVM运行时数据区
JDK/JRE/JVM三者之间的关系:
JDK包含JRE,JRE包含JVM.
JDK包含JRE以及Java文件的编译和其他的工具。
JRE包含JVM以及Java运行的一些核心类库。
其中方法区和heap(堆)是线程共享的,程序计数器/虚拟机栈/本地方法栈都是线程独有的。
程序计数器
可以看作是当前线程所执行字节码的行号指示器(指向当前线程正在执行的字节码的地址)。
在任何时刻,一个处理器只能执行一条线程,因此当多个线程交替执行时,为了使线程切换后能回到指定的位置,每个线程都会有一个独立的计数器,用来判断当前线程下一条需要执行的字节码指令。
此内存区域是Java虚拟机规范中没有任何outofmemory异常情况的区域。
Java虚拟机栈
虚拟机栈也是线程所私有的,每个方法在执行的时候都会创建一个栈帧(循环调用一个方法会创建多个栈帧)。
虚拟机栈是用来存储当前线程运行方法时所需要的数据、指令、以及返回地址的数据结构,单位为栈帧。
局部变量表:用来存放编译期可知的各种基本数据类型、对象引用以及returnAddress类型。一个局部变量空间长度时32位,所以long和double类型占用两个局部变量空间。局部变量表所需要的存储空间是在编译时期确定的,当进入一个方法时这个方法所需要在帧中分配多大的空间是确定的,在方法运行期间不会发生改变的。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackoverflowError异常;如果虚拟机栈可与扩展,在扩展时获取不到足够的内存空间,就会抛出outofMemoryError异常。
本地方法栈
与虚拟机栈所用类似,本地方法栈服务对象是用native修饰的方法。
Java堆
JMM(java内存模型)
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,所有的对象实例及数组都在堆上分配。
java堆中区域的分配下章
方法区
与堆一样是线程的共享区域,它用来存储被虚拟机加载的类信息、常量(1.7之后常量池放到堆里面了)、静态变量、即时编译器编译后的代码。
2:JVM的内存溢出异常
Java堆溢出
java堆用于存储对象实例,只要不断地创建对象,并且保证GC roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,当对象的数量达到堆的最大容量时,就会产生内存溢出异常。
要解决这个异常首先清楚是出现了内存泄漏还是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间。
什么意思呢?就是说,你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),而你自己出于某些原因不能再访问到那块内存(也许你把它的地址给弄丢了),这时候系统也不能再次将它分配给需要的程序。
内存溢出:就是内存不够。
内存溢出常见的原因有以下几种:
。内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
。集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
。代码中存在死循环或循环产生过多重复的对象实体;
。使用的第三方软件中的BUG;
。启动参数内存值设定的过小
会出现问题,自然也就有解决办法了。
4.内存溢出的解决方案:
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
第四步,使用内存查看工具动态查看内存使用情况。
其中,第三步重点排查以下几点:
。检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
。检查代码中是否有死循环或递归调用。
。检查是否有大循环重复产生新对象实体。
。检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
。检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收
虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError。
如果虚拟机在扩展栈是无法申请到足够的内存空间,将抛出OutofmemoryError。
在单线程下,如论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时,虚拟机都会抛出StackOverflowError。
方法区和运行时常量池溢出
本机直接内存溢出