首页 > 代码库 > 深入理解JVM

深入理解JVM

2. jvm性能调优都做了什么

3. 介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明

内存溢出和合理分配:http://blog.csdn.net/ye1992/article/details/9344807

http://flychao88.iteye.com/blog/2226205

http://www.itzhai.com/jvm-note-automatic-memory-management-mechanism.html#read-more

器池堆,栈栈区区

三个私有区:指令计数器,线程池,本地线程池

四个共享区:方法区,常量池,直接内存区,本地线程栈

堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的

在JVM中堆之外的内存称为非堆内存(Non-heap memory)

1.4.1、Java堆溢出

参数- XX:+ HeapDumpOnOutOfMemoryError

先通过内存映像分析工具(如 Eclipse Memory Analyzer) 对 Dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏( Memory Leak) 还是内存溢出( Memory Overflow)。

如果是内存泄露,可进一步通过工具查看泄露对象到 GC Roots 的引用链。

如果不存在泄露,换句话说,就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(- Xmx 与- Xms)

1.4.2、虚拟机栈和本地方法栈溢出

在 HotSpot 虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于 HotSpot 来说,虽然- Xoss 参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由- Xss 参数设定。

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。

限制于单线程中的操作,尝试了下面两种方法均无法让虚拟机产生 OutOfMemoryError 异常,尝试的结果都是获得 StackOverflowError 异常

在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是 StackOverflowError 异常。

2GB内存容量( 操作系统限制)减去 Xmx( 最大堆容量),再减去 MaxPermSize( 最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。

如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换 64 位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。

1.4.3、方法区和运行时常量池溢出

运行时常量池是方法区的一部分

在 JDK 1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过- XX: PermSize 和- XX: MaxPermSize 限制方法区大小,从而间接限制其中常量池的容量

" PermGen space", 说明运行时常量池属于方法区( HotSpot 虚拟机中的永久代)的一部分。

而使用 JDK 1.7 运行这段程序就不会得到相同的结果, while 循环将一直进行下去。

在 JDK 1.6 中, intern() 方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而由 StringBuilder 创建的字符串实例在 Java 堆上,所以必然不是同一个引用

而 JDK 1.7( 以及部分其他虚拟机,例如 JRockit) 的 intern() 实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此 intern() 返回的引用和由 StringBuilder 创建的那个字符串实例是同一个。

方法区用于存放 Class 的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

一个类要被垃圾收集器回收掉,判定条件是比较苛刻的。在经常动态生成大量 Class 的应用中,需要特别注意类的回收状况。

1.4.4、本机直接内存溢出(即系统内存)

DirectMemory 容量可通过- XX: MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最大值(- Xmx 指定)一样

如果读者发现 OOM 之后 Dump 文件很小,而程序中又直接或间接使用了 NIO, 那就可以考虑检查一下是不是这方面的原因。

 

4. 介绍GC 和GC Root不正常引用。

GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots集合且没有被GCroots引用(即不可达对象)的对象。

GC root集合有几下种:

    • Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots,.
    • Thread - 活着的线程
    • Stack Local - Java方法的local变量或参数
    • JNI Local - JNI方法的local变量或参数
    • JNI Global - 全局JNI引用
    • Monitor Used - 用于同步的监控对象
    • Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此就只有留给分析分员去确定哪些是属于"JVM持有"的了。

 

5. 自己从classload 加载方式,加载机制说开去,从程序运行时数据区,讲到内存分配,讲到String常量池,讲到JVM垃圾回收机制,算法,hotspot。反正就是各种扩展

6. jvm 如何分配直接内存, new 对象如何不分配在堆而是栈上,常量池解析

 

7. 数组多大放在 JVM 老年代(不只是设置 PretenureSizeThreshold ,问通常多大,没做过一问便知)

 

8. 老年代中数组的访问方式

 

9. GC 算法,永久代对象如何 GC , GC 有环怎么处理

 

10. 谁会被 GC ,什么时候 GC

 

11. 如果想不被 GC 怎么办

 

12. 如果想在 GC 中生存 1 次怎么办

13、普遍认为尽量不要使用finalize()进行资源释放,为什么?

在finalize时可能会导致对象复活

finalize的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize没有机会执行

一个糟糕的finalize会严重影响GC性能

回收对象-->>FinalizerThread的执行队列-->>

但是,在某些场合,使用finalize可以起到双保险作用:在MySQL的JDBC驱动中,com.mysql.jdbc.ConnectionImpl就实现

了finalize

 

14、GC的两种判定方法:引用计数与引用链

15、GC停顿原因,如何降低停顿?

http://www.itdadao.com/2016/02/20/402713/

16、内网中有一张图片,如何防止被泄漏?

深入理解JVM