对于java程序员来说,不用像C和C++的开发人员,需要开发人员手动的去参与内存管理,每一件事情都需要他们亲力亲为,java开发人员在虚拟机的自动管理内存机制的帮助下,不需要为new操作的对象去delete/free去释放内存,而且不容易存在内存泄漏和内存溢出等问题。不过正是因为将内存释放交给了虚拟机,所以一旦出现内存泄漏或者内存溢出等问题时,如果不了解虚拟机是怎么使用内存的,那将特别难排错哦。
java 虚拟机在执行程序时会把所管理的内存分成不成的区域,如下图所示:
其中,方法去和堆区是线程共享的,其他的则是隔离的数据区。
方法区:用于存放已经被虚拟机加载的类信息,常量,静态变量。java虚拟机规范对这个区域的限制非常宽松,除了和java堆一样不需要连续的内存和可以选择固定大小或可扩展外,还可以选择不实现垃圾回收。垃圾收集行为在这个区域是比较少出现的,但并非数据进入了这个区域就永远存在。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
1.基本类型:直接存在栈中
2.对象:创建一个对象分为两步,分为对象的声明和对象的实例化。对象的声明式存储在栈中的,对象实例化是存储在堆中的。
创建多个不同实例对象,例如 Student stu=new Student("小红",18);
Student s=stu;
在栈中创建了两个对象 stu, s,在堆中为stu对象的成员变量分配内存,stu,s都同时指向它。
3.包装类型:基本类型都对应的有包装类型,如int对应的有Integer,double对应Double类,基本类型的定义都是直接在栈中,如果用包装类来创建对象,就和普通对象一样
int i=0; 直接存储在栈中
Integer i=new Integer(5); //i对象数据存储在堆中,i的引用存储在栈中 ,通过栈中的引用来操作对象。
4.String 类型
String str=new String("Haha"); //这种创建方式就跟普通的存储方式是一样的。
String s="Haha";
常量池的字符串常量,不能重复出现,也就是说,在定义多个变量时,编译器首先去常量池中查找是否存在,如果不存在,则在常量池中创建一个新的字符串常量,如果存在,就不用创建了。
String s2="Haha";
String s3="Haha";
String s2=new String("Haha");
String s3=new String("Haha");
5.数组
当定义一个数组,int x[];或int[] x时,在栈内存中创建一个数组引用,通过该引用(即数组名)来引用数组。x=new int[3],将在堆内存中分配3个保存int型数据的空间,堆内存的首地址放在栈内存中,每个数组元素初始化为0;
java中的垃圾回收机制
1.什么是垃圾回收机制?
垃圾回收是一种动态存储管理技术,它自动的释放不在被程序引用的对象,按照特定的垃圾回收算法来实现资源自动回收的功能。当一个对象不再被引用时,内存回收它所占的内存,以便后来的新对象使用,以免造成内存泄漏。
2.java的垃圾回收有什么特点?
java语言不允许程序员直接控制内存空间的使用。内存空间的分配和回收都是由JRE负责在后台自动运行的,尤其是无用内存空间的垃圾回收操作,只能由运行环境提供的一个超级线程进行监测和控制。
3.垃圾回收是在什么时候进行的?
A.在系统空闲的时候或者内存空间不足的情况下。
B.GC又分为新生代GC(Minor GC)和 老年代GC(Major GC/Full GC)
新生代GC(Minor GC)指发生在新生代的垃圾收集动作,因为对象大多具有朝生夕灭的特性,存活时间短,所以Minor GC非常频繁,回收速度也比较快。新生代中有分为1个Eden区域和两个Survivor区域;
老年代GC(Major GC/Full GC):指发生在老年代中的GC,出现Major GC后,经常会伴随至少一次的Minor GC。Major GC存活时间长,速度会比Minor GC慢10倍以上。
对于新生代的触发条件:大多数情况下,直接在Eden区中进行分配。如果Eden区域没有足够的空间,那么就会发起一次Minor GC
对于老年代的触发条件:如果老年代没有足够的空间的话,那么就会进行一次Full GC
4.垃圾回收是对什么东西?
A.不使用的对象
B.超出作用域的对象或者是计数器为空的对象。
C.从GC roots开始搜索,搜索不到的对象。
D.从Roost搜索不到,并且经过一个标记清理后还没有复活的对象。
主要是根据可达性分析算法,如果一个对象可达,那么这个对象就不可以回收。对于可达性分析算法,它是通过一系列称为GC roots的对象作为起点,当一个对象到CG roots没有任何引用链相接的时候,那么这个对象就是不可达的。
5.垃圾回收做了什么事情?
主要做了清理对象,整理内存的工作。java堆分为新生代和老年代,采用了不同的回收方式。例如新生代采用了复制算法,老年代采用了标记整理的方法。在新生代中,分为一个Eden区域和两个Survivor区域,真正使用的是一个Eden区域和Survivor区域,GC的时候,会把存活的对象放到另一个Survivor区域中区,然后再把这个Eden和Survivor区域清楚。那么对于老年代,采用的是标记整理法,首先标记处存活的对象,然后把它移到另一端,这样也有利于减少内存碎片。
垃圾回收算法
1.引用计数器
一个对象被引用计数器加1,取消引用计数器减1。有点:简单。缺点:不能解决循环引用问题,比如A引用B,B引用A,但这两个对象没有被其他的任何对象引用,属于垃圾度对象,但是不能回收不了。
2.根搜索法
从GC Root往下搜索,对象不可达时,则可以回收,可达,则不能回收。
不可回收对象
可回收对象
哪些对象可以成为GC Root?
1.虚拟机栈中的引用变量
2.方法去中的静态属性引用的对象
3.方法区中的常量引用对象
4.本地方法栈中的引用对象
3.标记清理算法
分为两个阶段:标记和清理。标记阶段通过根节点标记所有可达对象,清楚不可达对象。缺点:因为清楚不可达对象之后剩余的内存不连续,会产生大量内存碎片,不利于大对象的分配。
4.复制算法
将内存空间分成相同的两部分,每次只用其中的一块,垃圾回收时,将正在使用的内存块中存活的对象复制到另一块空间中,然后清理正在使用空间中的所有对象。这种回收算法适用于新生代垃圾回收。 优点:垃圾回收对象比较多时复制的对象比较少,性能较好,还不容易产生内存碎片。 缺点:将系统内存折半了,可使用的内存减少。
5.标记压缩算法
是一种老年代回收算法,在标记清理上做了一些优化,首先从根节点开始标记所有不可达对象,然后将所有可达对象移动内存的另一端,最后清楚所有不可达对象。优点:不用将内存分成两部分,不会产生内存碎片。
6.分代算法
新生代使用复制算法,老年代使用标记清楚算法和标记压缩算法。java将内存分为新生代和老年代,所以GC垃圾回收分为新生代垃圾回收和老年代垃圾回收,当新创建对象时一般在新生代中分配内存空间,当新生代垃圾收集器回收几次之后仍然存活的对象会被移动到的老年代内存中,当大对象在新生代中无法找到足够的连续内存时也直接在年老代中创建。
7.分区算法:将整个堆空间分成个很多个连续的不同的小空间,每个小空间独立使用,独立回收,为了更好的控制GC停顿时间,可以根据目标停顿时间合理地回收若干个小区间,而不是整个堆空间,从而减少DC停顿时间。