内存管理技术
来源:互联网 发布:网络综合布线施工顺序 编辑:程序博客网 时间:2024/05/16 15:00
任何语言都会涉及到内存的管理和使用,很多语言要求开发人员自己进行所有内存的管理工作,如c++等。而内存管理要求的技术难度很大,很多开发人员不能很好地完成,同时也成为意向沉重的负担。
java则不同,其为内存管理提供的一套完整的解决方案——垃圾收集机制,大大减轻了开发人员编写内存管理代码的负担,减少了出错的机会,简化了开发。
一、程序中的“垃圾“”是什么
所谓垃圾,是指在内存中不再有用的对象,其占用的内存应该释放。将不再有用的对象清除出内存的工作称为“垃圾收集”。
1、对象成为“垃圾”的条件
(1)对于非线程对象来说,当所有的活动线程都不可能访问到该对象时,该对象便成为“垃圾”。
(2)对于线程对象来说,除了满足第一条标准之外,还要求此线程本身已经死亡或者还处于新建状态。
2、单个对象的情况
对于非线程的耽搁情况来说,使其成为垃圾的方法很简单,只要将指向该对象的所有引用不再指向该对象即可。
(1)将指向该对象的引用设置为null值。
//创建字符串对象,并将引用s指向该对象String s=new String();//将引用s 的值设为null值s=null;
(2)将引用指向别的对象。
//创建字符串对象,并将引用s指向该对象String s=new String();//将引用s指向新的对象s=new String();
(3)随着语句块或者方法的退出局部引用消亡。
//创建一个函数,当函数执行完之后,局部对象,成为垃圾被回收public void fun(){String s=new String("xiao");System.out.println(s);}
3、多个对象的孤岛情况
有引用指向的一样可能是垃圾,关键看这些对象能不能被活动线程访问到。
class Island{//引用类型为自己的成员变量public Island brother;}//创建3个Island类的对象Island i1 =new Island();Island i2 =new Island();Island i3 =new Island();//分别为三个对象中的成员变量赋值i1.brother=i2;i2.brother=i3;i3.brother=i1;//将Island的三个引用分别设置为null值i1=null;i2=null;i3=null;
二、“垃圾”收集器
让对象成为垃圾的工作是由来发人员来完成的,而java中清理垃圾的工作是由垃圾收集器自动完成,不需要开发人员做很多的工作。
1、垃圾收集器的基本介绍
开发人员需要做的工作仅仅是将不需要的对象根据规则“标识”为垃圾,而垃圾收集器何时收集垃圾,如何收集垃圾都不需要开发人员关心。其实,垃圾收集器就是一个后台守护线程,在内存充足的情况下,其优先级很低,一般不会出来运行,当垃圾充斥着内存,严重影响程序执行时,其优先级会提高,并出来运行收集垃圾,清理内存。正是因为如此,垃圾收集器的运行时间是没有保障的。
2、申请垃圾收集器的运行
垃圾收集器的运行是由系统自动决定的,但是这并不是说开发人员一点都不能干预,开发人员可以通过调用特定的方法申请垃圾收集器运行。当然,在收到申请后,如果系统不是很繁忙,垃圾收集器一般都会运行,但这也没有保障。
Runtime类
Runtime类的对象,通过自身的getRuntime()函数获得。
Runtime类的几个常用的方法
方法签名功能public static Runtime getRuntime()该方法将返回一个当前运行程序相关的Runtime类的对象,相当于一个对象工厂。public void gc()申请垃圾收集器运行public long totalMemory()该方法将返回当前JVM使用的总内存量,单位为字节public long freeMemory()该方法将返回当前JVM中可使用的内存量,单位为字节代码如下:
1 public class javaTest { 2 3 public static void main(String[] args) throws InterruptedException { 4 Runtime rt=Runtime.getRuntime(); 5 rt.gc(); 6 Thread.sleep(100); 7 System.out.println("没有创建对象之前的剩余内存:"+rt.freeMemory()); 8 for(int i=0;i<100000000;i++){ 9 new String("小帅哥");10 }11 Thread.sleep(100);12 System.out.println("创建100个字符串对象后剩余的内存:"+rt.freeMemory());13 rt.gc();14 Thread.sleep(100);15 System.out.println("申请垃圾收集器运行后的剩余内存:"+rt.freeMemory());16 }17 }
运行结果:
1 没有创建对象之前的剩余内存:63609344 2 创建100个字符串对象后剩余的内存:58639088 3 申请垃圾收集器运行后的剩余内存:63609272
三、如何收集“垃圾”
对象作为垃圾清理出内存之前,可能需要进行一些扫尾的工作,在java中,这些扫尾工作的代码可以编写在被收集对象的finalize()方法中。(java中finalize()函数类似于c++中的析构函数~)
1、finalize重写
finalize方法来自Object()类,因此,每个类都有此方法。在一个对象被作为垃圾收集之前,垃圾收集器会首先调用垃圾对象的finalize()方法,然后再清除垃圾对象。
protected void finalize() throws Throwable
(1)由该方法的访问限制可以看出,因为所有的类都直接间接继承Object类,所以所有的类都可以具有该方法。
(2)对象核实被进行垃圾收集是没有保障的,有可能在整个应用程序裕兴的生命周期中一直没有进行垃圾回收,因此需要保证执行的特定的处理代码不应编写在此方法中。
(3)重写该方法时一般不但要编写自己类特定的处理代码,还应该使用“super.finalize();”调用父类的finalize()方法。因为自己特定类的对象也是一个父类的对象,父类对象的清理代码也应该执行,除非有意识的修改父类的清理对象才不需要调用父类的finalize()方法。
(4)如果没有重写该方法,则在垃圾收集时会调用父类的方法,直至追溯到Object类的finalize()方法。
1 //父类 2 class People{ 3 4 @Override 5 protected void finalize() throws Throwable { 6 super.finalize(); 7 System.out.println("这是people类的finalize()方法!"); 8 } 9 }10 //子类11 class Man extends People{12 @Override13 protected void finalize() throws Throwable {14 super.finalize();15 System.out.println("这是Man类的finalize()方法!");16 } 17 }18 public class javaTest {19 20 public static void main(String[] args) throws InterruptedException {21 //获得Runtime类对象,用于后面的申请垃圾收集器调用方法的22 Runtime rt=Runtime.getRuntime();23 //创建People对象24 Man m=new Man();25 //将对象引用指向null,让People类的对象成为垃圾26 m=null;27 //申请垃圾收集器运行28 rt.gc();29 Thread.sleep(100); 30 }31 }
运行结果:
1 这是people类的finalize()方法! 2 这是Man类的finalize()方法!
2、finalize安全问题
对象被执行垃圾收集前会调用其finalize()方法,如果仅仅这样,就可能带来安全问题。如果在finalize()方法中编写一些恶意的代码,在每次执行finalize()方法时使自己的对象不满足垃圾的条件,就可以组织垃圾收集,产生恶意的常驻对象。为了避免这种情况,在java中规定,一个对象的生命周期中的finalize()方法最多被执行一次。也就是说,若第一次执行垃圾收集时执行此方法阻止了垃圾收集,第二次再执行垃圾收集时不会再执行此方法,直接清除垃圾对象。
1 class Man { 2 static public Man p; 3 @Override 4 protected void finalize() throws Throwable { 5 super.finalize(); 6 p=this; 7 System.out.println("这是Man类的finalize()方法!"); 8 if(p!=null){ 9 p=null;10 System.gc();11 Thread.sleep(100);12 }13 14 } 15 }16 public class javaTest {17 18 public static void main(String[] args) throws InterruptedException {19 //获得Runtime类对象,用于后面的申请垃圾收集器调用方法的20 Runtime rt=Runtime.getRuntime();21 //创建People对象22 Man m=new Man();23 Man m2=new Man();24 //将对象引用建立引用循环25 m=null;26 27 //申请垃圾收集器运行28 rt.gc();29 Thread.sleep(100);30 31 }32 }
运行结果:
这是Man类的finalize()方法!
当一个对象,在其生命周期中只能执行一次finalize()函数。
四、非线程“垃圾”
通过下面一段代码,让大家了解一下非线程“垃圾”回收:
1 class Man { 2 3 private String name; 4 //构造函数 5 public Man(String name) { 6 this.name=name; 7 } 8 @Override 9 protected void finalize() throws Throwable {10 super.finalize(); 11 System.out.println(this.name+"对象被当作垃圾回收了!"); 12 } 13 }14 public class javaTest {15 16 public static void main(String[] args) throws InterruptedException {17 //获得Runtime类对象,用于后面的申请垃圾收集器调用方法的18 Runtime rt=Runtime.getRuntime();19 //创建People对象20 Man m=new Man("帅哥1");21 Man m2=new Man("美女2");22 //将对象引用建立引用循环23 m=null;24 m2=null; 25 //申请垃圾收集器运行26 rt.gc();27 Thread.sleep(100);28 }29 }
运行结果:
1 美女2对象被当作垃圾回收了!
2 帅哥1对象被当作垃圾回收了!
分析:特别注意“垃圾”的回收顺序,类似c++中的析构函数的执行顺序,先执行后被定义为垃圾的对象,是一个反的顺序。
五、线程“垃圾”
1 class Man extends Thread { 2 3 private String name; 4 //构造函数 5 public Man(String name) { 6 this.name=name; 7 } 8 @Override 9 public void run() {10 // TODO Auto-generated method stub11 super.run();12 System.out.println("开始执行"+this.name+"线程!");13 try {14 Thread.sleep(1000);15 } catch (InterruptedException e) {16 // TODO Auto-generated catch block17 e.printStackTrace();18 }19 System.out.println("即将执行完线程"+this.name);20 }21 @Override22 protected void finalize() throws Throwable {23 super.finalize(); 24 System.out.println(this.name+"对象被当作垃圾回收了!"); 25 } 26 }27 public class javaTest {28 29 public static void main(String[] args) throws InterruptedException {30 //获得Runtime类对象,用于后面的申请垃圾收集器调用方法的31 Runtime rt=Runtime.getRuntime();32 //创建People对象33 Man m=new Man("帅哥");34 Man m2=new Man("美女");35 Man m3=new Man("人妖");36 m.start();37 //将对象引用建立引用循环38 m=null; 39 //对无引用但活着的线程,申请垃圾收集器运行40 System.out.println("**********对无引用但活着的线程进行垃圾回收**********");41 rt.gc();42 Thread.sleep(2000);43 44 m2=null;45 //对无引用但处于新建状态的线程,申请垃圾收集器运行46 System.out.println("**********对无引用但但处于新建状态的线程进行垃圾回收**********");47 rt.gc();48 Thread.sleep(2000);49 50 //执行线程m3(人妖)51 m3.start();52 //设置等待时间,为了使线程执行完成为死亡的线程53 Thread.sleep(1500);54 m3=null;55 //对无引用死亡的线程,申请垃圾收集器运行56 System.out.println("**********对无引用死亡的线程进行垃圾回收**********");57 rt.gc();58 Thread.sleep(100); 59 }60 }
运行结果:
1 **********对无引用但活着的线程进行垃圾回收********** 2 开始执行帅哥线程! 3 即将执行完线程帅哥 4 **********对无引用但但处于新建状态的线程进行垃圾回收********** 5 帅哥对象被当作垃圾回收了! 6 美女对象被当作垃圾回收了! 7 开始执行人妖线程! 8 即将执行完线程人妖 9 **********对无引用死亡的线程进行垃圾回收**********10 人妖对象被当作垃圾回收了!
结果分析:第5行的结果,是因为当线程运行结束后,成为死线程而被回收。结果显示:
对于线程对象来说,当所有的活动线程都不可能访问到该对象时,还要求此线程本身已经死亡或者还处于新建状态,则才会进行垃圾收集。活线程不会被垃圾收集~
- 内存池管理技术
- 内存管理技术
- oracle内存管理技术
- ARC内存管理技术
- 详谈内存管理技术
- 内存管理技术
- 内存管理技术
- day05内存管理技术
- 内存管理技术详细说明
- windows底层内存管理技术
- Cocos2d-x内存管理技术
- STL和内存管理技术
- C++内存管理技术内幕
- linux段页式内存管理技术
- linux物理内存管理技术
- C++内存管理技术内幕
- 内存管理-内存池技术
- D语言中的内存管理技术
- PrintWriter用法简析
- javascript实现常见的算法
- Hadoop安装
- C++——复制构造函数的形参为什么要是const引用
- 解决 eclipse 启动报错 org.eclipse.ui.ide.multiFilter
- 内存管理技术
- 如何向AOSP中添加根证书
- .net获取当前url各种属性(文件名、参数、域名 等)
- Docker方式搭建Gogs环境
- 自动生成Makefile
- nginx负载均衡高可用
- java读取 *.properties文件中所有对象
- 最短路径 自己写的一个很简单的模板 dijkstra算法
- 设计模式C++观察者模式