前面一篇文章介绍了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. [Typescript] Introduction to Generics in Typescript

    If Typescript is the first language in which you've encountered generics, the concept can be quite d ...

  2. angularjs于directive声明scope说明何时以及如何使用对象修饰符

    于angular我们定义directive方法.查看 return { restrict: 'AE', scope: {}, template: '<div></div>', ...

  3. Android学习笔记(十一)——从意图返回结果

    从意图返回结果 startActivity()方法调用还有一个活动,但并没有返回结果给当前活动.此时如想从一个活动中回传数据,就要使用startActivityForResult()方法. 点此获取完 ...

  4. Android 网络编程与通信协议

    大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,而Android中主要提供了两种方式来进行HTTP操作, HttpURLConnection和HttpClient.这两种方式都 ...

  5. 判断两条直线的位置关系 POJ 1269 Intersecting Lines

    两条直线可能有三种关系:1.共线     2.平行(不包括共线)    3.相交. 那给定两条直线怎么判断他们的位置关系呢.还是用到向量的叉积 例题:POJ 1269 题意:这道题是给定四个点p1, ...

  6. JPush 极光推送 消息推送 实例

    简介 官网:https://www.jpush.cn/ 极光推送(JPush)是一个端到端的推送服务,使得服务器端消息能够及时地推送到终端用户手机上,让开发者积极地保持与用户的连接,从而提高用户活跃度 ...

  7. 富文本编辑器CKEDITOR的使用配置(问题注解)

    CKEDITOR是一款非常轻便的富文本编辑器,如上图:参考网上的使用方法,我以.net为例,在aspx页面使用, 准备工作:首先要下载控件包,将解压后的整个文件夹添加到项目根目录. 第一步:引用js, ...

  8. 数据转换错误,java.lang.NumberFormatException: null

    今天写项目时报了一个数组转换错误的异常,让我找了半天5555 -_- . 一般出现这种错误呢,情况都是parseInt转换所触发的,像下面这行代码 int factorOneInt = Integer ...

  9. 执行start-dfs.sh后,datenode没有启动

    Hadoop2.2.0启动异常 – Incompatible clusterIDs 2014年08月29日 ⁄ 综合 ⁄ 共 2399字 ⁄ 字号 小 中 大 ⁄ 评论关闭 今天启动Hadoop2.2 ...

  10. 静默安装ORACLE【weber出品必属精品】

    安装配置系统环境安装linux ,所有服务都不选择,只是选择安装开发工具,不要安装防火墙(当然也可以在后面关闭) 打开终端,执行如下命令,检查安装包,没有的都要安装 make, glibc, liba ...