实际案例:只要点击Myeclipse第三方jar包的的类方法的Open Declaration,就会报内存不足异常,myeclipse僵死无响应
问题1:提示并不是说”memory“不足,而是“Heap memory”,加了memory类型限定,说明内存细分为多种,这只是其中一种内存不足,那么JVM是如何划分管理内存的呢?
问题2:提示不但指明是”heap memory“内存不足,而且还具体指明是”heap memory“中的Tenured Gen内存不足,说明“heap memory”还进一步被划分成多块,每块单独管理。那么”heap memory“是如何被划分的呢?
问题3:提示给出了解决方法建议,推荐了配置参数,说明可以对myeclipse用到的内存进行配置。那么在哪里配置?如何配置?能配置哪些内容?
配置路径:myeclipse安装目录下的myeclipse.ini文件
注意:配置jvm的地方有很多,但是配置myeclipse启动运行的地方就此一处,不可能等myeclipse启动打开后再设置,因为配置就的就是myeclipse如何启动
配置文件格式说明:
-VM配置myeclipse使用的具体JVM,启动myeclipse时,首先检查是否有这个配置,若无,在安装目录下找jre,若无,从系统环境变量找jre,若无,启动报错。
-vmargs声明要对JVM内存进行配置,若不写该参数,则其它相关配置无效,全部按默认值启动
对虚拟机内存的配置分”-“开头和”-XX:"开头两大类
“-”开头:整体分配内存用,如-Xms初始堆大小、-Xmx最大堆大小、-Xmn新生代大小
“-XX:"开头的配置分为三大类:
1、性能参数:如禁止在运行期显式地调用System.gc()、关闭新生代收集担保、在Full GC前触发一次Minor GC等
2、功能开关:如新生代占整个堆内存的最大值、Perm(俗称方法区)占整个堆内存的最大值、新生代和年老代的堆内存占用比例等
3、调试参数:如打印GC详细信息、跟踪常量池的变化、打印信息输出文件等等
问题4:提示推荐的配置中并没有Tenured Gen内存大小,那么Tenured Gen的内存大小默认是从哪个配置分配多少的呢?
1、堆总大小:-Xms初始值、-Xmx堆最大值,若两项值相同,则是固定大小,不可扩展
2、新生代和老年代各自大小:只对新生代设置,剩下的为老年代所有
按大小设置:-Xmn新生代的总大小,固定不变,推荐使用;老年代大小=堆总大小-新生代大小;Sun官方推荐配置为整个堆的3/8
按比例设置:-XX:NewRatio=n设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4;默认为2
3、新生代内部Eden和Survior大小
survivor会分为同等大小的两部分:from survivor和to survivor
只能通过比例设置:-XX:SurvivorRatio=n年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5。默认为8,即Eden占8/10,Survior每块占1/10,一共占2/10.
问题5:tenured属于堆中的老年代内存,存放的是长期存活的对象集,现在myeclipse没有运行任何程序,那么这些对象是由谁创建,用来干什么?
1、myeclipse本身就是一个java程序,也需要部署在JVM上,而且是一个相对很大的程序,很多服务是常驻在内存中,需要实时监听、接收和处理用户发起的请求。
2、这些堆中的对象就是myeclipse这个java程序运行创建的,且很大一部分是长期存活,所以会大量占用tenured老年代的堆内存
问题6:myeclipse自己运行就已经占用JVM如此大的内存,若要在上面增加weblogic或者tomcat,而且weblogic等容器中还要部署多个程序,jvm怎么能撑住?
myeclipse启动运行的JVM和myeclipse执行开发人员的程序启动的JVM不是同一个,单独执行java main方法、启动tomcat、启动weblogic,都会启不同的JVM实例。
比如在myeclipse中debug一个test.java,就能看到操作系统中启动了两个JVM。
通过JVM分析工具能够看到这时有三个JVM实例
单独run方法,使用的jvm就是myeclipse用的jvm,只是各自启了一个jvm实例,独立分配内存
第一个是myeclipse的,堆内存分配了600M,耗费近200M
第二个是test执行时的,分配内存60M,仅用了几M。
第三个是该分析工具本身的(也是java程序,需要JVM运行)
test.java执行结束,对应的虚拟机终止
java虚拟机能获得多少内存进行管理和使用?
操作系统会给java进程分配内存
1、初始值:为java配置的最小值,即java内存配置参数的-Xms、-XX:PermSize等配置的值之和
2、最大值:受操作系统单线程最大可申请内存大小的限制,如32位windows最大单线程内存为2G
java虚拟机如何分配获得的内存?
拿到的内存主要分为如下四部分:
1、java虚拟机栈和本地方法栈
2、程序计数器
3、方法区
4、运行时数据区域
其中其中虚拟机栈、本地方法栈和程序计数器,是线程私有内存
方法区和运行时数据区是线程共享内存
java虚拟机如何使用和回收具体用途的内存?
java虚拟机栈
大小=(大致)java虚拟机可用总内存-方法区内存-运行时数据区内存
作用:每创建一个线程,就会给该线程分配一个栈
虚拟机栈描述的是java方法执行的内存模型,存放的数据后到先出
线程每执行一个方法,就会给方法生成一个栈帧,并将该栈帧压入栈顶,方法执行完后出栈
【补充栈帧入栈和出栈的示意图】
栈帧用于存储方法的局部变量、操作数栈、动态链接、方法出口等信息
【补充执行字节码命令时如何使用操作数栈和局部变量的示例】
【补充字节码指令表】http://blog.csdn.net/ygc87/article/details/12953421
单个虚拟机栈的大小及设置方法:
-Xoss设置本地方法栈大小,hotspot虚拟机中无实际作用,两种栈合并在一起
-Xss设置虚拟机栈大小,一般默认512K
栈大小会影响最大支持的线程数,栈越大,能创建的线程越少,反之,能创建的线程越多
栈和栈帧的生命周期:
栈的生命周期跟线程相同,线程终止,栈实时回收
栈帧的生命周期跟方法相同,方法开始,栈帧压入栈顶,方法结束,栈帧出栈
如何程序验证栈的内存大小?
验证栈:即验证单个线程的栈深度;
栈可以动态扩展,但是有上线
栈存放的是栈帧,即测试可以最大能放多少个栈帧
栈帧的大小取决于对应的方法,变量多,需要内存就越大
栈对应线程,线程就是执行一系列方法,每个方法就是一个栈帧,所以要把栈堆满,就得方法调方法,通过无限递归调用可实现
程序计数器:
多线程实际上并不是真正的同时并行,一个固定时刻,只有一个线程会执行,虚拟机按一定优先级给所有线程分配时间片。
要实现不同线程之间的切换,就得记录上一个挂起的线程的恢复点
程序计数器就是记录线程执行的字节码行号,每个线程都有一个程序计数器
内存大小:
唯一
minor GC新生代的GC回收
full GC全局回收
一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。
串行收集器:
DefNew:是使用-XX:+UseSerialGC(新生代,老年代都使用串行回收收集器)。
并行收集器:
ParNew:是使用-XX:+UseParNewGC(新生代使用并行收集器,老年代使用串行回收收集器)或者-XX:+UseConcMarkSweepGC(新生代使用并行收集器,老年代使用CMS)。
PSYoungGen:是使用-XX:+UseParallelOldGC(新生代,老年代都使用并行回收收集器)或者-XX:+UseParallelGC(新生代使用并行回收收集器,老年代使用串行收集器)
garbage-first heap:是使用-XX:+UseG1GC(G1收集器)