Java微观探源(二)_一个例子看懂JVM内存分配和回收


实际案例:只要点击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执行结束,对应的虚拟机终止

myeclipse和weblogic可以使用不同的jdk,启动后的虚拟机也各不相同,其中第二个是myeclipse,第四个是weblogic
问题7:为何myeclipse正常运行时只耗费了一两百M内存,而打开第三方jar包类的方法的open declaration时,jvm tenured老年代堆内存瞬间就满了?
点击前的tenured内存占用情况:
点击后的tenured内存占用情况:
整个测试过程现象如下:
1、正常启动后,查看本地编写的java代码的类方法定义或实现,myeclipse不会假死,内存也无变化
2、若查看第三方jar的类,myeclipse持续一会僵死无反应,此时内存占用额度快速上升,说明myeclipse在加载外挂程序,且程序很大
3、等恢复反应一段时间后,手工执行full GC(会回收tenured的失效对象),但是内存占用额基本不变,说明加载的程序是常驻程序
4、此后无论在哪个工程中,查看第三方jar的类的方法定义或实现,都很快,说明加载的程序一直存在,且在起作用
推论:myeclipse有一个按需加载的快速查询第三方jar包类信息的外挂程序,若有需要,就加载,且常驻内存

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收集器)





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值