一、背景:
最近在空闲时间接触了《深入java虚拟机 jvm高级特性与最佳实践》决定需要记录一下鄙人对java内存分析的一点小的感想,以此深入理解java,也是对自己的文字表达和技术深入打下基础。
二、java内存分析图(java1.6)
- 用画图软件简单的画了一幅java1.6的内存分析图:
-
在我看来对于一个java开发人员,我们既拥有每个对象的所有权,又担负这每个对象的接生和送终。
-
内存分析过程:
当我们需要使用一个对象时,使用java的new关键字时,首先会在栈内存中分配一块空间,保存对象的引用,这时候会在堆空间分配一块内存,保存new关键字创建的对象,首先会进入新生代的Eden区,如果开发人员没有及时的释放,并且没有被java的GC回收,这时候就会进入幸存代s0,当s0充满时,s0会回收一部分没用的资源,其余的传给s1,s0和s1进行交替运行,在交替几次后,还没有被gc回收,JVM便自动认为这块为常驻内存,正式进入了老年代。 -
注意:1.s0和s1为大小相等的两块区域,但是在实际的使用中,一次只能使用一份。
2.栈的内存的开辟和结束都是由java中的{}决定的,{一开便开启一块内存,}便结束了栈内存
的生命。
三、名词解释:
- 程序计数器:
为了切换线程后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,如果线程执行的为java代码。程序计数器这块内存就存储着正在执行的虚拟机字节码的指令位置,如果执行的为native方法,则存储为空,注意:这块区域是唯一一个没有规定outofMemoryError情况的区域。 - 永久代
存放已经被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码的区域。
四、java1.7和1.8内存的一些小改变。
- 对于java版本的迭代,内存也发生了一些变化,对于新出的java9,甚至java10还没有进行研究,目前先说一说java1.7和Java1.8的变化吧。
- java1.7
1、对于java1.7,最大的变化就是字符串常量池离开了他的家乡永久代,举家搬迁到了堆外内存。 - java1.8
1、java1.8更加过分了,对永久代进行了房屋拆迁,退耕还林,完全干掉了永久代的存在,将代码区/静态块等老居民都送到了堆外的直接内存和他们的老邻居字符串常量池间面了,将全局计算器直接放到了对象身上,随身携带,取名为MetaStore。 - 原因
因为java虚拟机规范对永久代的限制非常宽松,除了java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,同时对垃圾回收机制有不实现的选择,由于这个原因这个区域的内存回收很不理想,但是这个区域的回收也是很有必要的,所以很大程度上造成了内存的泄露。可能java的开发人员也熟知了这个问题,所以在1.8的版本去除了永久代。
五、一些常见的内存泄露的分析。
- 栈内存的异常
stackOverFlowError:线程请求的栈深度大于虚拟机所允许的深度时,一般的方法死循环或错误的 使用递归等会造成栈溢出
outofMemoryError:虚拟机在扩展栈时无法申请到足够的内存空间,抛出的异常。 - 堆内存溢出
out of Memory Heap space:一般为老年代溢出,当年轻代中的内存用完,下次new的时候,会直接被创建到老年代中。 - out of Memory Perm space:永久代溢出
- out of Memory Meta space:直接内存溢出
六、总结
- 对于java的内存结构,本次就介绍到这里,也是对我自己的一个理解的深入,可能很浅显,但也是对自己思考后成果的一种记录吧,加油!!!











网友评论