前面一篇文章介绍了Java虚拟机的体系结构和内存模型,既然提到内存,就不得不说到内存泄露。众所周知,Java是从C++的基础上发展而来的,而C++程序的很大的一个问题就是内存泄露难以解决,尽管Java的JVM有一套自己的垃圾回收机制来回收内存,在许多情况下并不需要java程序开发人员操太多的心,但也是存在泄露问题的,只是比C++小一点。比如说,程序中存在被引用但无用的对象:程序引用了该对象,但后续不会或者不能再使用它,那么它占用的内存空间就浪费了。

我们先来看看GC是如何工作的:监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,当该对象不再被引用时,释放对象(GC本文的重点,不做过多阐述)。很多Java程序员过分依赖GC,但问题的关键是无论JVM的垃圾回收机制做得多好,内存总归是有限的资源,因此就算GC会为我们完成了大部分的垃圾回收,但适当地注意编码过程中的内存优化还是很必要的。这样可以有效的减少GC次数,同时提升内存利用率,最大限度地提高程序的效率。

总体而言,Java虚拟机的内存优化应从两方面着手:Java虚拟机和Java应用程序。前者指根据应用程序的设计通过虚拟机参数控制虚拟机逻辑内存分区的大小以使虚拟机的内存与程序对内存的需求相得益彰;后者指优化程序算法,降低GC负担,提高GC回收成功率。

通过参数优化虚拟机内存的参数如下所示:

Xms

初始Heap大小

Xmx

java heap最大值

Xmn

young generation的heap大小

Xss

每个线程的Stack大小

上面是三个比较常用的参数,还有一些:

XX:MinHeapFreeRatio=40

Minimum percentage of heap free after GC to avoid expansion.

XX:MaxHeapFreeRatio=70

Maximum percentage of heap free after GC to avoid shrinking.

XX:NewRatio=2

Ratio of new/old generation sizes. [Sparc -client:8; x86 -server:8; x86 -client:12.]-client:8 (1.3.1+), x86:12]

XX:NewSize=2.125m

Default size of new generation (in bytes) [5.0 and newer: 64 bit VMs are scaled 30% larger; x86:1m; x86, 5.0 and older: 640k]

XX:MaxNewSize=

Maximum size of new generation (in bytes). Since 1.4, MaxNewSize is computed as a function of NewRatio.

XX:SurvivorRatio=25

Ratio of eden/survivor space size [Solaris amd64: 6; Sparc in 1.3.1: 25; other Solaris platforms in 5.0 and earlier: 32]

XX:PermSize=

Initial size of permanent generation

XX:MaxPermSize=64m

Size of the Permanent Generation. [5.0 and newer: 64 bit VMs are scaled 30% larger; 1.4 amd64: 96m; 1.3.1 -client: 32m.]

下面所说通过优化程序算法来提高内存利用率,并降低内存风险,完全是经验之谈,仅供参考,如有不妥,请指正,谢谢!

1.尽早释放无用对象的引用(XX = null;)

看一段代码:

public List<PageData> parse(HtmlPage page) {
        List<PageData> list = null;
        try {
            List valueList = page.getByXPath(config.getContentXpath());
            if (valueList == null || valueList.isEmpty()) {
                return list;
            }
            //需要时才创建对象,节省内存,提高效率
            list = new ArrayList<PageData>();
            PageData pageData = new PageData();
            StringBuilder value = new StringBuilder();
            for (int i = 0; i < valueList.size(); i++) {
                HtmlElement content = (HtmlElement) valueList.get(i);
                DomNodeList<HtmlElement> imgs = content.getElementsByTagName("img");
                if (imgs != null && !imgs.isEmpty()) {
                    for (HtmlElement img : imgs) {
                        try {
                            HtmlImage image = (HtmlImage) img;
                            String path = image.getSrcAttribute();
                            String format = path.substring(path.lastIndexOf("."), path.length());
                            String localPath = "D:/images/" + MD5Helper.md5(path).replace("\\", ",").replace("/", ",") + format;
                            File localFile = new File(localPath);
                            if (!localFile.exists()) {
                                localFile.createNewFile();
                                image.saveAs(localFile);
                            }
                            image.setAttribute("src", "file:///" + localPath);
                            localFile = null;
                            image = null;
                            img = null;
                        } catch (Exception e) {
                        }
                    }
                    //这个对象以后不会在使用了,清除对其的引用,等同于提前告知GC,该对象可以回收了
                    imgs = null;
                }
                String text = content.asXml();
                value.append(text).append("<br/>");
                valueList=null;
                content = null;
                text = null;
            }
            pageData.setContent(value.toString());
            pageData.setCharset(page.getPageEncoding());
            list.add(pageData);
            //这里 pageData=null; 是没用的,因为list仍然持有该对象的引用,GC不会回收它
            value=null;
            //这里可不能 list=null; 因为list是方法的返回值,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现、排除
        } catch (Exception e) {
        }
        return list;
}

2.谨慎使用集合数据类型,如数组,树,图,链表等数据结构,这些数据结构对GC来说回收更复杂。

3.避免显式申请数组空间,不得不显式申请时,尽量准确估计其合理值。

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

5.尽量避免强制系统做垃圾内存的回收,增长系统做垃圾回收的最终时间

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

7.尽量在合适的场景下使用对象池技术以提高系统性能

Java虚拟机内存优化实践的更多相关文章

  1. Java虚拟机内存模型和volatile型变量

    Java虚拟机内存模型 了解Java虚拟机的内存模型,有助于我们明白为什么会发生线程安全问题. 上面这幅图是<深入理解Java虚拟机-JVM高级特性与最佳实践>的书中截图. 线程共享的变量 ...

  2. Java虚拟机内存分配详解

    简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数 ...

  3. Java------------JVM(Java虚拟机)优化大全和案例实战

    JVM(Java虚拟机)优化大全和案例实战 堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Ge ...

  4. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  5. Citrix 服务器虚拟化之十三 Xenserver虚拟机内存优化与性能监控

    Citrix 服务器虚拟化之十三   Xenserver虚拟机内存优化与性能监控 XenServer的DMC通过自动调节运行的虚拟机的内存,每个VM分配给指定的最小和最大内存值之间,以保证性能并允许每 ...

  6. 如何设置Java虚拟机内存以适应大程序的装载

    Java虚拟机对于运行时的程序所占内存是有限制的,当我们的项目或者程序很大时,往往会照成内存溢出. 举个例子: public class SmallTest1 { public static void ...

  7. 打包apk java 虚拟机内存不足

    解决方案:在android->sdk->build-tools-android-version 中有个 dx.bat dx.bat --dex 命令的dx.bat脚本有这样一句代码 REM ...

  8. Java虚拟机-内存tips

    java虚拟机内存可以分为独占区和共享区. 独占区:虚拟内存栈.本地方法栈.程序计数器. 共享区:方法区.Java堆(用来存放对象实例). 程序计数器 比较小的内存空间,当前线程所执行的字节码的行号指 ...

  9. Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)

    Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...

随机推荐

  1. iPhone 各版本屏幕分辨率

    参考:http://www.paintcodeapp.com/news/iphone-6-screens-demystified

  2. 大数据笔记06:大数据之Hadoop的HDFS(文件的读写操作)

    1. 首先我们看一看文件读取: (1)客户端(java程序.命令行等等)向NameNode发送文件读取请求,请求中包含文件名和文件路径,让NameNode查询元数据. (2)接着,NameNode返回 ...

  3. 进程ps、kill 、grep

    linux上进程有5种状态: 1. 运行(正在运行或在运行队列中等待) 2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号) 3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有 ...

  4. 人工智能2:智能Agent

    一.Agent基本定义 基于理性行为的Agent是本书人工智能方法的核心.Agent由传感器.执行器两个重要元件组成,具有与环境交互的能力,其能力是通过分析感知序列,经过Agent函数映射到相应的行动 ...

  5. Genymotion 模拟器 VirtualBox

    准备 介绍: 1.Genymotion安卓模拟器其实不是普通的模拟器,严格来说,genymotion是虚拟机,被网传定义为模拟器,加载APP的速度比较快,操作起来也很流畅.2.Genymotion依赖 ...

  6. HUD 2846 Repository

    /* 开始想耍小聪明 直接map搞 代码短 好理解 空间够 恩 很好 就是 map慢死了 T了 */ #include<iostream> #include<cstdio> # ...

  7. Android 5.1 Camera 架构学习之Camera初始化

    Android Camera 采用C/S架构,client 与server两个独立的线程之间(CameraService)使用Binder通信. 一 CameraService的注册. 1.手机开机后 ...

  8. Ubuntu 11.10开启root用户登陆

    以管理员身份运行 #sudo gedit /etc/lightdm/lightdm.conf 将里面改成 "autologin-user=root" 就可以以root用户登录了

  9. C#垃圾回收

    析构方法: 我们知道引用类型都有构造方法(constructor),相对应的也有一个析构方法(destructor).顾名思义,构造方法,就是在创建这个对象时,要执行的方法.例如,我们可以通过构造方法 ...

  10. web基础--html

    WebBasic 1.web应用体系 课程大纲 1.web基础:做网页     2.结构:         a.html             勾勒网页结构及内容         b.css     ...