内存回收就是释放掉没有任何引用的对象所占的内存空间(从root搜索不到,而且经过第一次标记、清理后,仍然没有复活的对象)。
首先,要判断怎样的对象可以被回收?这里有2种方法:
1.采用标记计数的方法:
给内存中的对象给打上标记,对象被引用一次,计数就加1,引用被释放了,计数就减一,当这个计数为0的时候,这个对象就可以被回收了。当然,这也就引发了一个问题:循环引用的对象是无法被识别出来并且被回收的。所以就有了第二种方法:
2.采用根搜索算法:
从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的
判断完了哪些对象是没用的,这样就可以进行回收了
最简单的,就是直接清空那个需要被回收的对象。但是这又出现了一个问题,就是内存会被分为一块一块的小碎片。
为了解决这个问题,可以采用第二种方法,就是在之前的基础上将存活的对象给整理一下,使他们变成一个连续的内存,从而释放出连续的较大的内存空间。
还有一中回收方法就是采用复制的办法:将内存分为2块,一块用来存放对象,另一块用来放着,当存放对象的那块满了以后就将上面存活的对象给复制过来,然后在这块内存上工作,并且将之前的内存清空,当自己这块满了以后再复制回去,如此反复。
比较效率的一中做法是将以上的几种方法给结合起来。
首先将内存分块,分为新生代,老年代和永久代。
永久代用来存放代码,等一些基本不改变的数据,
新生代用来存放刚产生的一些对象,新生代又可分为3块。分别为Edon区,Survivor0,survivor1,刚产生的对象是放在Edon区中,当这个区块放满了以后就将其存活的部分复制到survivor0块中,并且将Edon区中的数据清空,等到survivor0满了就将其中的存活的数据放到survivor1中,清空survivor0,垃圾回收到了一定次数还未被回收的对象,就可以放到老年区。一般来说,刚才产生的对象大多是要在下一次垃圾回收的时候就要被回收掉的,只有一小部分对象会被保留下来,这些被保留下来的对象都是比较稳定的,所以在老年区中的对象回收方法可以采用整理的方法,而在Edon区等新生代中采用复制的方法比较好。
万一被问到还有呢。。想了想,还可以扯些其他方面的:
垃圾回收他是在虚拟机空闲的时候或者内存紧张的时候执行的,什么时候回收不是由程序员来控制的,这也就是Java比较耗内存的原因之一。
还有在垃圾回收的时候当检测到对象没有用了,需要被回收的时候并不会马上被回收,而是将其放入到一个准备回收的队列,去执行finalize方法。。然等到下次内存回收的时候要是他还是没有被任何人引用的话,就将其给回收了。(如果在finalize方法中重新给对象加个引用,这样对象是有可能不会被回收的)不过finalize方法不推荐使用,他跟C++中的析构函数不同,我们既不能确定什么时候他回被回收,也不能保证这个方法一定会被执行。
Java虚拟机规范将JVM虚拟机所管理的内存分为几部分?
1. 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行字节码的行号指示器。是线程私有,生命周期与线程相同。
2. Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。
Java虚拟机栈描述的是Java方法(区别于native的本地方法)执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动作链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
3. 本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机所使用到的Native方法服务。
4. 方法区
(3)有哪些方法可以判断一个对象已经可以被回收,JVM怎么判断一个对象已经消亡可以被回收?
①引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。
Java语言没有选用引用计数法来管理内存,因为引用计数法不能很好的解决循环引用的问题。
②根搜索算法
在主流的商用语言中,都是使用根搜索算法来判定对象是否存活的。
GC Root Tracing 算法思路就是通过一系列的名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连,即从GC Roots到这个对象不可达,则证明此对象是不可用的。
比如上图,左边的对象都是存活的,右边的都是可以回收的。
(4)那些对象可以作为GC Roots?
虚拟机栈(栈帧中的本地变量表)中的引用的对象
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI(Native方法)的引用对象
(5)Java代码编译的结果是什么?
是字节码文件.class
(6) 怎么理解Java语言的平台无关性?语言无关性?通过什么方法实现的?
Java是平台无关的语言是指用Java写的应用程序不用修改就可在不同的软硬件平台上运行。 Java主要靠Java虚拟机(JVM)在目标码级实现平台无关性。JVM是一种抽象机器,它附着在具体操作系统之上,本身具有一套虚机器指令,并有自己的栈、寄存器组等。但JVM通常是在软件上而不是在硬件上实现。(目前,SUN系统公司已经设计实现了Java芯片,主要使用在网络计算机NC上。另外,Java芯片的出现也会使Java更容易嵌入到家用电器中。)JVM是Java平台无关的基础,在JVM上,有一个Java解释器用来解释Java编译器编译后的程序。Java编程人员在编写完软件后,通过Java编译器将Java源程序编译为JVM的字节代码。任何一台机器只要配备了Java解释器,就可以运行这个程序,而不管这种字节码是在何种平台上生成的(过程如图1所示)。另外,Java采用的是基于IEEE标准的数据类型。通过JVM保证数据类型的一致性,也确保了Java的平台无关性。 实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。基于安全方面的考虑,Java虚拟机规范要求在Class文件中使用许多强制性的语法和结构化约束,但任一门功能性语言都可以表示 为一个能被Java虚拟机所接受的有效的Class文件。作为一个通用的、机器无关的执行平台, 任何其他语言的实现者都可以将Java虚拟机作为语言的产品交付媒介。
(7)Java中的static变量和static方法在JVM运行中内存的分配管理有什么不同和一般变量方法?
静态对象 非静态对象 拥有属性: 是类共同拥有的 是类各对象独立拥有的 内存分配: 内存空间上是固定的 空间在各个附属类里面分配 分配顺序: 先分配静态对象的空间 继而再对非静态对象分配空间,也就是初 始化顺序是先静态
(9)在heap中没有类实例的时候,类信息还存在于JVM吗? 存在于什么地方?
java对象要包含的基本数据至少要有两部分:1、类以及超类的实例声明的实例变量;2、指向类数据的引用,jvm需要通过此引用找到该对象的(可能存在的)方法表、类型信息。其中类型信息包括类型基本信息、常量池、字段信息、方法信息、类变量
HotSpot JVM (下简称JVM)的内存管理,详见:http://www.iteye.com/topic/894148
原文:http://2091535.blog.51cto.com/2081535/1910793