上面我们说了下程序计数器,栈,现在我们在根据这个来了解下另外一个重要的结构,堆(heap),有人可能疑惑了为什么不说本地方法栈,这里我们了解了java栈,其实本地方法栈和java栈是一致的,我们当你调用java方法的时候,这时候进入的是java栈,当你调用native的方法的时候,进入的是本地方法栈。
回到整体,我们继续来说这个堆(heap)。
首先明确下,堆是所有线程共享的,在虚拟机启动的时候就创建了,他的任务就是存放对象的实例,对象的实例都在这个分配内存,当你内容变化的时候,这个实例是不会去改变的,因为他存放的对应的是你这个对象的实例的内存地址。
那这样说,如果现在我new一个对象,这个对象的实例就放在这个堆里面对应的内存的地址,但是内存是有限的,我new确是无限的,那我该怎么做呢?我也没有见过谁写这个释放内存的代码啊?
这里我们就要去明白一个问题对上述的回答,如果在c/c++是需要我们去写free/delete的代码块来释放的,但是java针对这块做了实现,就是我们的这个jvm了,在堆中分配的内存是很多,但是现在我们jvm会对这一块垃圾回收的频率很高的,继续上图。
这就是我们堆的图,我们现在先记住这张图
下面就是看图说话了,从上图中我们可以了解到两条信息,1.这个有三个区,新生区,养老区,永久储存区 2.新生区里面有三个区:伊甸区(Eden)、幸存0区(From Survior)、幸存1区(To Survior)3.新生区可以向养老区去"靠拢"
图看完了,现在我们就针对我们看出来的信息具体的看看,写一篇属于我们jvm堆的作文。
第一条就是我们看图说出来的:堆内存逻辑上分为三部分:新生+养老+永久
新生区里面有三个区:伊甸区(Eden)、幸存0区(From Survior)、幸存1区(To Survior),这个我记得当时老师说的例子就是,现在我们需要征收士兵入伍,我们接受了训练,马上要上战场了,我们被放到了新生区,并且是在伊甸区(Eden),第二天战斗打响了,由于敌人攻势较猛(minor gc),一个连队一百人,只有四、五个人存活了下来,但是不能这样就回去啊,所以在次征收士兵,刚在存活的就去了幸存去0区(From Survior)当班长了,新来的士兵继续在伊甸区(Eden),战斗再次打响,这时候敌人整备还是很好,班长也得上战场,这样打下去,新兵存活下来的就成为了班长,但是不能所有的士兵都当班长啊,也有班长在战斗中牺牲啊,当存活班长的人数我们快达到了幸存0区(From Survior)一定程度的时候,我们将幸存1区(To Survior)和幸存0区(From Survior)两个身份调换下,0区成为了1区,1区成为了0区。当我们士兵经历了15次炮火(minor gc)的洗礼,这时候可能就要退役了,退役了我们的善待功勋啊,就让他们去了养老区,但是人有生老病死(full gc),但是这里的死亡是比上战场的伤亡低很多的,一旦这个在养老区,我们存活了下来,我们就去了永久存储区。
上面这个例子可以帮我们了解下这个大致的流程。具体的我们后面会在垃圾回收再次分析下。
这里有个名词我们了解下(OOM),是啥呢?就是我们大家都可能遇到的(OutOfMemoryError)
如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:
(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)