在做Java开发的时候常用的JVM内存管理有两种,一种是堆内存,一种是栈内存。堆内存主要用来存储程序在运行时创建或实例化的对象与变量,例如:我们通过new MyClass()创建的类MyClass的对象。而栈内存则是用来存储程序代码中声明为静态(或非静态)的方法。下面我给大家举个例子:

  1. 代码
  2. public class Test{
  3. static Vector list = new Vector();
  4. static void makeThings(){
  5. Object object = new Object();
  6. list.add(object);
  7. }
  8. public static void main(){
  9. makeThings();
  10. }
  11. }

就拿上面的例子来说,放在栈内存中的有:main,makeThings,放在堆内存中有:Test,list,object。

JVM中对象的生命周期大致可以分为7个阶段:创建阶段、应用阶段、不可视阶段、不可到达阶段、可收集阶段、终结阶段与释放阶段。

1.创建阶段:

(1)为对象分配存储空间。

(2)开始构造对象。

(3)递归调用其超类的构造方法。

(4)进行对象实力初始化与变量初始化。

(5)执行构造方法体。

还有就是你在创建对象的时候需要注意的地方:

(1)避免在循环体中创建对象,即使该对象占用内存空间不大。

(2)尽量及时使对象符合垃圾回收标准。

(3)不要采用过深的继承层次。

(4)访问本地变量优于访问类中的变量。

2.应用阶段:

在应用阶段涉及到4个引用:

(1)强引用:是指JVM内存管理器从根引用集合出发遍寻堆中所有到达对象的路径。

(2)软引用:是具有较强的引用功能,只有当内存不够的时候,才回收这类内存,因此内存足够的时候,不会被回收。

(3)弱引用:弱引用与软引用对象的最大不同在于:GC在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用来说,GC总是进行回收。

(4)虚引用:主要用于辅助finalize函数的使用。虚引用主要适用于以某种比Java终结机制更灵活的方式调度pre-mortem清除操作。

3.不可视阶段:

先看一段代码:

  1. 代码
  2. public void process(){
  3. try{
  4. Object obj = new Object();
  5. obj.doSomething();
  6. }
  7. catch(Exception e){
  8. e.printStackTrace();
  9. }
  10. while(isLoop){
  11. //这个区域对于obj对象来说已经是不可视的了
  12. //因此下面的代码在编译时会引发错误
  13. obj.doSomething();
  14. }
  15. }

如果一个对象已使用完了,应该主动将其设置为null,可以在上面的代码行obj.doSomething();下添加代码行obj=null;这样一行代码强制将obj对象置为空值,这样做的意义就是帮助JVM及时的发现这个垃圾对象,并且可以及时的回收该对象占用的系统资源。

4.不可到达阶段:

处于不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用,这些对象通常是指多有线程栈中的临时变量,所有已装载的类的静态变量或者对本地代码接口(JNI)引用。

 5.可收集阶段、终结阶段与释放阶段:

当对象处于这个阶段的时候,可能处于下面三种情况:

(1)垃圾回收器发现该对象已经不可到达。

(2)finalize方法已经被执行。

(3)对象空间已被重用。

当对象处于上面三种清空的时候,虚拟机就可以直接将该对象回收了。

析构方法finalize

前面我们说了JVM的垃圾回收机制和JVM中对象的生命周期,今天给大家讲个方法,叫做析构方法finalize,我想搞过C++的人都知道,而且是内存管理技术中相当重要的一部分。但是,在Java中好像没有这个概念,这是因为,理论上JVM负责对象的析构(销毁与回收)工作,finalize是Object类中的一个方法,并且是protected,由于所有的类都继承了Object对象,因此,就都隐式的继承了改方法,不过可以重写这个方法,如果重写此方法,最后一句必须写上super.finalize()语句,因为finalize方法没有自动实现递归调用。那我们在什么时候要重写它呢?当有一些不容易控制并且非常重要的资源时,要放到finalize方法中,例如:一些I/O的操作,数据的连接等等,这些资源的释放对整个应用程序是非常关键的。

我先让大家看一段代码:

  1. public class TestA{
  2. Object obj = null;
  3. public TestA(){
  4. obj = new Object();
  5. System.out.println("创建obj对象");
  6. }
  7. protected void destroy(){
  8. System.out.println("释放obj对象");
  9. obj = null;
  10. //释放自身所占用的资源
  11. }
  12. protected void finalize() throws java.long.Throwable{
  13. destroy();
  14. //递归调用超类中的finalize方法
  15. super.finalize();
  16. }
  17. }

finalize方法最终是由JVM中的垃圾回收器调用的,由于垃圾回收器调用finalize的时间是不确定或者不及时的,调用时机对我们来说是不可控的,因此我们可以在自己的类中声明一个destory()方法,在这个方法中添加释放系统资源的处理代码,但是还是建议你将对destroy()方法的调用放入当前类的finalize()方法体中,因为这样做更保险,更安全。

静态变量

我们知道类中的静态变量在程序运行期间,其内存空间对所有该类的对象实例而言是共享的,为了节省系统内存开销、共享资源,应该将一些变量声明为静态变量。通过下面的例子,你就会发现有什么不同。

代码一:

  1. public class MemoryTest {
  2. static class Data{
  3. private int week;
  4. private String name;
  5. Data(int i, String s){
  6. week = i;
  7. name = s;
  8. }
  9. }
  10. Data weeks[] = {
  11. new Data(1,"monday"),
  12. new Data(2,"Tuesday"),
  13. new Data(3,"Wednesday"),
  14. new Data(4,"Thursday"),
  15. new Data(5,"Friday"),
  16. new Data(6,"Saturday"),
  17. new Data(7,"Sunday")
  18. };
  19. public static void main(String[] args) {
  20. final int N = 20000;
  21. MemoryTest test = null;
  22. for (int i = 0; i <=N; i++) {
  23. test = new MemoryTest();
  24. }
  25. System.out.println(test.weeks.length);
  26. }
  27. }

代码二:

  1. public class MemoryTest {
  2. static class Data{
  3. private int week;
  4. private String name;
  5. Data(int i, String s){
  6. week = i;
  7. name = s;
  8. }
  9. }
  10. static Data weeks[] = {
  11. new Data(1,"monday"),
  12. new Data(2,"Tuesday"),
  13. new Data(3,"Wednesday"),
  14. new Data(4,"Thursday"),
  15. new Data(5,"Friday"),
  16. new Data(6,"Saturday"),
  17. new Data(7,"Sunday")
  18. };
  19. public static void main(String[] args) {
  20. final int N = 20000;
  21. MemoryTest test = null;
  22. for (int i = 0; i <=N; i++) {
  23. test = new MemoryTest();
  24. }
  25. System.out.println(test.weeks.length);
  26. }
  27. }

我想大家应该发现上面那两个类的区别了吧!

代码一会在内存中保存20000个weeks的副本,而代码二则在内存中保存1个weeks的副本,然后共享该副本,这样的话就不会造成内存的浪费。

虽然静态的变量能节约大量的内存,但是并不是所有的地方都适合用,建议大家在下列条件都符合的情况下,尽量用静态变量:

(1)变量所包含的对象体积较大,占用内存较多。

(2)变量所包含的对象生命周期较长。

(3)变量所包含的对象数据稳定。

(4)该类的对象实例有对该变量所包含的对象的共享需求。

如果变量不具备上述特点,建议不要轻易使用静态变量,以免弄巧成拙。

最后,再提一点内存的优化,就是有关对象的重用,比如:对象池和数据库连接池等。那样的话,是很节约内存空间的,不过,在用的时候要考虑各个方面,比如:运行环境的内存资源的限制等。为了防止对象池中的对象过多,要记得清除。

内存管理有许多技巧和方式

其实内存管理有许多技巧和方式,在这,我给大家介绍一下。

(1)要尽早的释放无用对象的引用。如果,该对象不用了,你可以把它设置为null。但要注意,如果该对象是某方法的返回值,千万不要这样处理,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现,因此这时很难及时抓住、排除NullPointerException异常。

(2)尽量少用finalize函数。因为它会加大GC的工作量,因此尽量少用finalize方式回收资源。

(3)如果需要使用经常用到的图片,可以使用soft应用类型(也就是转换为软引用类型),它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory。

(4)注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对于GC来说,回收更为复杂。另外,要注意那些全局变量,静态变量,这些对象往往容易引起悬挂对象,造成内存浪费。

(5)尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其子类的构造器时造成不必要的内存资源浪费。

(6)尽量避免强制系统做垃圾内存回收(通过显式调用方法System.gc()),增长系统做垃圾回收的最终时间,降低系统性能。

(7)尽量避免显式申请数组空间,当不得不显式申请数组空间时尽量准确的估计出其合理值,以免造成不必要的系统内存开销。

(8)尽量在做远程方法调用(RMI)类应用开发时使用瞬间值变量,除非远程调用端需要获取该瞬间值变量的值。

(9)尽量在合适的场景下使用对象池技术以提高系统的性能,缩减系统内存开销,但是要注意对象池的尺寸不易过大,及时清除无效对象释放内存资源,综合考虑应用运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。

虽然,这些技巧提高不了多少性能,但是,在嵌入式开发,或者要求性能比较高的系统中却很有用。

感觉写的很好收藏了,原文出自:http://www.360doc.com/content/11/0808/13/1542811_138888668.shtml

JVM的内存管理机制的更多相关文章

  1. JVM的内存管理机制-转载

    JVM的内存管理机制 一.JVM的内存区域 对于C.C++程序员来说,在内存管理领域,他们既拥有每一个对象的"所有权",又担负着每一个对象生命开始到终结的维护责任. 对Java程序 ...

  2. JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇<Java自动内存管理机制——Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

  3. JVM自动内存管理机制——Java内存区域(上)

    一.JVM运行时数据区域概述 Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄 ...

  4. JVM自动内存管理机制--读这篇就GO了

    之前看过JVM的相关知识,当时没有留下任何学习成果物,有些遗憾.这次重新复习了下,并通过博客来做下笔记(只能记录一部分,因为写博客真的很花时间),也给其他同行一些知识分享. Java自动内存管理机制包 ...

  5. 一文洞悉JVM内存管理机制

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习导图: 一.为什么要学习内存管理? Java与C++之间有一堵由内存动态分配和垃圾回收机制所围成的高墙,墙 ...

  6. Java虚拟机内存管理机制

    自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区 ...

  7. 你必须了解的java内存管理机制(三)-垃圾标记

    本文在个人技术博客不同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩... 相关链接(注:文章讲解JVM以Hotspot虚拟机为例,jdk版本为1.8) ...

  8. JVM介绍&自动内存管理机制

    1.介绍JVM(Java Virtual Machine,Java虚拟机) JVM是Java Virtual Machine的缩写,通常成为java虚拟机,作为Java可以进行一次编写,到处执行(Wr ...

  9. 【JVM】5、JVM内存管理机制

    转自:http://blog.csdn.net/lengyuhong/article/details/5953544 近期看了看Java内存泄露的一些案例,跟原来的几个哥们讨论了一下,深入研究发现JV ...

随机推荐

  1. codevs 1228 苹果树

    dfs序+线段树 #include<iostream> #include<cstdio> #include<cstring> #include<algorit ...

  2. POJ 3422 Kaka's Matrix Travels (K取方格数:最大费用流)

    题意 给出一个n*n大小的矩阵,要求从左上角走到右下角,每次只能向下走或者向右走并取数,某位置取过数之后就只为数值0,现在求解从左上角到右下角走K次的最大值. 思路 经典的费用流模型:K取方格数. 构 ...

  3. Android Studio 学习 - 基本控件的使用;Intent初学

    Android Studio学习第三天. 今天主要学习 1. RadioButton.CheckBox.RatingBar.SeekBar等基础控件的使用. 结合Delphi中相类似的控件,在这些基本 ...

  4. Eclipse 打开编辑文件所在文件夹方法

    一个便捷的方法在eclipse的菜单中,依次点击Run->External Tools-> External Tools configurations添加一个新的工具 OpenContai ...

  5. mysql同步 小问题

    由于历史遗留问题,我们的MySQL主从库的表结构不一致,主库的某个表tableA比从库表tableA少了一个字段. 当尝试在主库上更改表结构时,这行alter语句会随着binlog同步到从库,如果从库 ...

  6. Node.js的循环依赖

    我们知道在实际编程过程中,要尽可能的减少或者规避循环依赖情况的发生.但在现实环境中,有时却不得不产生循环依赖.Node.js不提倡使用循环依赖,但真有如此情况发生时Node.js也有办法解决.这篇博文 ...

  7. Bootstrap-select:美化原生select

    官网:http://silviomoreto.github.io/bootstrap-select/ 1.下载zip 2.html代码 <select class="selectpic ...

  8. jQuery autoComplete 样式

    前提:使用了jQuery-ui 官网:http://jqueryui.com/autocomplete/ /*** autocomplete ***/ .ui-widget-content { bac ...

  9. JAVA遍历一个文件夹中的所有文件

    在实际项目中给定一文件夹,得到这个文件夹下所有的文件这样的需求并不是很多,更多的是查找或是删除某一具体的文件 import java.io.File; import java.util.ArrayLi ...

  10. [Everyday Mathematics]20150302

    $$\bex |p|<\frac{1}{2}\ra \int_0^\infty \sex{\frac{x^p-x^{-p}}{1-x}}^2\rd x =2(1-2p\pi \cot 2p\pi ...