《深入理解Java虚拟机》学习笔记

本篇是《深入理解Java虚拟机-Java 高级特性与最佳实践》学习笔记,周志明著,Understanding the JVM-Advanced Features and Best Practices,机械工业出版社,2011.6出版。


重温Java JVM知识,重点学习了与日常开发工作相关性最大的“自动化内存管理”模块,对Java容器优化、内存问题解决很有帮助;习惯了从互联网看电子书,难以集中和记忆,现在找几本纸质书重温,可以很清静、很安静的理解和消化,受益匪浅。


自动内存管理机制


Java和C++之间有一睹由动态内存分配和垃圾收集机制组成的墙,里面的人想出来,外面的人想进去。

在Java里面调用System.gc,会立刻引起一次FullGC和YougGC。


1、Java内存区域与内存溢出异常


虚拟机运行时数据区 存储内容 可能发生的溢出错误及优化措施 描述
堆(Heap) 对象实例 OutOfMemoryError: Java heap space,通过调节参数-Xms2048m -Xmx2048m,设置堆的初始内存和最大内存进行优化 在虚拟机启动时创建,是VM所管理的内存中最大的一块,是垃圾收集器管理的主要区域,也被称作“GC堆”,堆是所有线程共享的
虚拟机栈(VM Stack) Java方法执行的内存模型:每个方法被执行时,都会同时创建一个栈帧(Stack Frame)用于存储局部变量表(基本数据类型和对象引用类型),操作数栈,动态链接,方法出口等信息 单线程可能会报StackOverflowError,多线程可能会报OutOfMemoryError,对应参数是-Xss2m,一般不设这个参数,而是采用默认值 生命周期和线程相同,每个方法被调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程,有时会通过减小栈和堆大小的方式优化,以保证可以开更多线程,栈是线程私有的
本地方法栈(Native Method Stack) 本地方法栈和虚拟机栈所发挥的作用非常相似,其区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈为虚拟机使用到的Native方法服务。
在Sun HotSpot虚拟机中,本地方法栈和虚拟机栈是在一起的。
方法区(Method Area) 类信息、常量、静态变量、即时编译器编译后的代码 OutOfMemoryError: PermGen space,通过调节参数-XX:PermSize=1024m -XX:MaxPermSize=1024m,设置方法区的初始内存和最大内存进行优化 有个别名叫Non-Heap,对于HotSpot来说,也被称作“永久代”(Permanent Generation),运行时常量池(Runtime Constant Pool)是方法区的一部分,方法区是所有线程共享的
程序计数器(Program Counter Register) 当前线程所执行的字节码的行号计数器 不会溢出 是线程私有的
直接内存(Direct Memory) 直接内存不是虚拟机运行时数据区的一部分,是使用Native函数库直接分配的堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用,目的是在一些场景中显著提高性能,因为避免了再Java对和Native堆中来回复制数据;可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值(-Xmx指定)一样;会报溢出错误:OutOfMemoryError
三大商业虚拟机 Sun HotSpot,Bea Jrockit,IBM J9,前两个虚拟机都已经被Oracle收购,Sun HotSpot是使用最普遍的虚拟机。


JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。

应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。


如果我们需要知道,JVM虚拟机的默认参数配置,可以使用命令查看:

java -XX:+PrintFlagsInitial | grep Ratio

我们可以看到,新生代和老年代的默认比值是2;Survior和Eden的默认比值是8.

JVM实用参数(三)打印所有XX参数及值


补充一个案例,如下:

如果写了一个普通的死循环,没有发现,则会占用cpu时间,但是可能不会导致内存溢出的情况,因为可能对于堆和栈空间都没有太大影响;

但是如果写了个递归死循环,循环递归,则马上会报StackOverFlow,因为随着递归函数的调用,大量的本地变量被声明,很快,栈空间就满了;

另,对于不同的JDK,可能会变化,比如String的常量String.intern(),在JDK6中存放在“方法区”的“运行时常量池”中,但是在JDK7中,就存放在堆中了,所以,很多细节也是在一直变化中的。。。

参考网址:

http://blog.csdn.net/kthq/article/details/8618052

http://www.open-open.com/lib/view/open1324736648468.html


2、垃圾收集器与内存分配策略


对象已死的确定方法:

  使用案例 描述
引用计数算法 COM、AS3、Python、Squirrel 很难解决对象之间的相互循环引用问题
根搜索算法 Java、C#、Lisp  
在Java语言里,可以作为GC Roots的对象包括:(虚拟机栈中的/方法区中的类静态属性和常量/本地方法栈中JNI)引用的对象
Java对象的finalize方法,优先级低,运行代价高昂,不确定性大,无法保证各个对象的调用顺序,建议大家完全可以忘记Java中还有这个方法的存在;这个方法最多只会被垃圾收集器调用一次。

垃圾收集算法:

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值