首先了解JVM内存模型
创建的对象存在堆里,栈,先从main方法进入,调用run方法,逐一向上执行,run方法中的this指代car,调用fire方法
然后fire方法先结束,再run,最后到main方法终止,栈的特点:先执行的方法后终止。
JVM的作用 :
举个反例:C语言通过malloc()和free()这两个方法来实现分配,释放内存空间,一方面,对已释放的内存,未曾分配
的内存做释放,否会造成死机;另一方面,对不再使用的内存不释放,会浪费系统内存,甚至内存枯竭
如:
int main(int argc,char* argv[])
{
int *p;//定义一个int类型的指针
p = (int *)malloc(4);//分配4个连接的内存单元给指针p
free(p);//释放指针p所指向的内存单元
printf("Hello ");
return 0;
}
而Java中就存在JVM垃圾收集机制,解决这类问题;
一、JVM如何发现垃圾:
1、引用计数法:
当一个对象被引用了一次就+1,结束引用就-1,引用为0的时候就会被当作垃圾被处理(当main方法退出后,方法里所有的引用都为0)
缺:无法判断相互引用的垃圾对象
对于引用计数法的缺陷提出了另一个方法
2、根搜索算法(GC Root):
没有根或者没有被有根对象引用的对象将会被垃圾回收。(根:如变量、常量等)
二、回收、清理垃圾
1.标记清除算法(已淘汰):
将存活对象,可回收,未使用,用不同标记标记起来,然后一块一块的清除;
缺:留下内存碎片,影响大文件存放,使用效率低;
之后提出了分段复制算法;
2.分段复制算法:
改进了标记清除算法,回收后将同一状态的对象复制在一起,并各分一半。
缺:太过于麻烦和死板
3.标记整理算法
改进分段复制算法,提高了效率,没有太严格一定要分为一半了。
3.分代收集算法
把堆里区域分为好几代,刚new出的对象存在Eden中,生命周期长的对象就复制放在生存区(利用分段复制法发过去),from、to区相互复制替换,生存区被多次引用的对象又被复制到老年代(Eden被垃圾回收的机率很高,生存区频率就比Eden机率小一点,老年代频率再度降低,正因为这样的处理方式提高了程序的效率)
Minor GC :把Eden区域清理垃圾(小范围,新生代)
Major GC : 大范围的清理新生代和中年代
Full GC:完整的垃圾回收
三、高效代码技巧
1、尽量不要在循环中使用:异常处理机制(try-catch)、new对象;
2、把频繁使用的短命对象缓存起来;
3、尽可能使用栈变量(方法内局部变量)
4、用线程池,连接池,不要自己创建;
举个例:内存泄漏和内存溢出
内存泄漏:垃圾回收不掉的对象
内存溢出:内存不够,导致程序崩溃
//演示内存泄漏
public class MemoryLeak{
static List<Student> list = new ArrayList<Student>();
public static void main(String[] args){
for(int i = 0;i<1000000;i++){
Student stu = new Student();
list.add(stu);
//stu = null;//内存泄漏,无法回收
}
list = null;//可被GC回收
while(true){
Thread.sleep(20000);//休眠20秒
}
}
}
解决方法:
调整堆内存大小:-Xms256M –Xmx512M
调整栈内存大小:-Xss2048k
调整方法区大小:-XX:MetaspaceSize=64M
-XX:MaxMetaspaceSize=128M
调整方法区大小:-XX:PermSize=64M
–XX:MaxPermSize=128M
Java 7及以前适用