`
ezerg
  • 浏览: 274190 次
  • 性别: Icon_minigender_1
  • 来自: 石家庄
社区版块
存档分类
最新评论

Java 程序的优化笔记

阅读更多
系统的优化是一个比较宽泛的话题,涉及到硬件、软件和网络的优化等,软件又包括操作系统、数据库和应用软件优化等。
优化没有一个特定的规则,如何进行优化往往以特定的架构设计下为前提。所以说好的架构设计不仅方便整个系统后期的维护和扩展,还有对系统的优化也会有很大的影响。
以下只针对 Java 语言分别从虚拟机、技术框架和代码编写三个方面来简单说明一下。

一、Java 虚拟机
在保障稳定的前提下,尽量使用新的虚拟机版本,每次虚拟机版本的提升都会带来性能的提高。例如新版本中 Java 反射的性能也有很大的提高。

使用新垃圾收集机制,因为虚拟机主要的性能瓶颈之一就是垃圾回收,不论是年经代还是年老代的回收。JDK 6.0 增加了新的并发的回收机制。
默认情况下虚拟机运行在 Client 模式下,一般服务器下建议使用 Server 模式,只需要在命令行加上 -server 参数。
JVM给了三种收集器:串行收集器、并行收集器、并发收集器。以下列出主要的配置参数:
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。

根据实际的情况,优化虚拟机配置参数,主要考虑吞吐量优先还是响应时间优先。来设置虚拟机整个内堆的大小,再分别调整年轻代、年老代和持久代的大小。
响应时间优先的应用年轻代尽可能设大,年老代使用并发收集器。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

以下是一个 Linux 环境 JDK 6.0  下虚拟机参数的配置:
-Xms256m -Xmx256m -Xmn96m -Xss256k -XX:PermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection
-XX:CMSInitiatingOccupancyFraction=80 -XX:LargePageSizeInBytes=8m -XX:+DisableExplicitGC -XX:+UseFastAccessorMethods -Dfile.encoding=GBK

垃圾回收统计信息:
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:PrintHeapAtGC
-Xloggc:filename


二、Java 新技术

1、使用 JDK 本身的新技术。每次虚拟机版本的提升不仅会带来性能的提高,同时也会有新的技术,新技术往往会很好的解决以前的技术难题。
1.4 版的新 IO 接口,通过非阻塞的 IO 极大提高的磁盘文件读写和网络通信的性能
5.0 版的泛型、Annotation等等都给编程带来极大的方便。另外,java.util.concurrent 包的类极大提高并发程序的效率。
6.0 版整体性能提升的同时,也增加了新的并发的垃圾回收机制,使得大并发的情况下程序运行的反应时间更快。

2、使用第三方的新技术。
还有其它三方的工具类来代替 JDK 本身的工具类。例如 Kryo 代替本身续列化;Joda-Time 代替本身的 Calendar 操作;使用 fastutil 代替本身的容器类;Logback 代替本身的日志记录等等。
第三方的软件来解决特定的问题。例如字节码操作代替反射;选择类似 FreeMarker 模板技术代替 JSP 显示页面;使用 proxool 等数据库连接池技术等等。


三、Java 最佳实践编码

1、尽量使用位操作是直接对二进制位的操作,速度最快。
一个整数 i 乘以 2 的操作正常应该为 i * 2 ,实际上我们可以使用 i << 1 ,当然 i 除以 2 的是右移操作
判断一个整数 i 是否为奇数:(i & 1) == 1
类似多个选项的选择问题,例如多项选择题选项 A B C D  ,程序中可以如下设置:
public class Test {
	public static final int A = 0x0001; // 二进制 0001
	public static final int B = 0x0002; // 二进制 0010
	public static final int C = 0x0004; // 二进制 0100
	public static final int D = 0x0008; // 二进制 1000

	public static void main(String[] args) {
		// 根据选择项的不同进行或操作,例如结果选择 A B C
		int i = Test.A | Test.B | Test.C;
		System.out.println(Integer.toBinaryString(i));
		// 根据结果判断选择项进行与操作
		int j = i & Test.A;
		System.out.println(Integer.toBinaryString(j));
	}
}

这样操作不仅结果存储很小,同时操作也很方便。


2、容器类对象的处理。也就是 List 和 Map 对象的优化。
如果容器的对象数量确定,尽量使用数组代替容器类,并且数组复制使用 System.arraycopy() 方法
使用List 或 Map 时尽量设置初始值。
List l = new ArrayList(100); // 如果不指定初始值,默认的大小是 10
如果 size 不够则会再创建一个新的数组,大小为旧数据的 1.5 倍加1,并将旧数据复制过去。
	   // 扩展 List 的代码
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
    	    if (newCapacity < minCapacity)
		newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);

如果不设置大小,随着 List 的增加它将频繁的复制数据。
Map m = new HashMap(100, 0.8); // 如果不指定初始值,默认的大小分别是 16 和 0.75
当大小超过 初始值乘以加载因子,即超过 100*0.8=80 时就会重新创建内部结构,扩大为原来的 2 倍。
	  // 扩展 Map 的代码
        if (size++ >= threshold)
            resize(2 * table.length);

如果不设置初始值,随着 Map 的增加它也会频繁的复制数据。
取得 Map 容器中的 Key 和 Value 使用 Entry
		Map m = new HashMap();
		Iterator it = m.entrySet().iterator();
		while (it.hasNext()) {
			Entry entry = (Entry) it.next();
			request.setAttribute((String)entry.getKey(), entry.getValue());
		}


3、使用 StringBuffer 和 StringBuilder 代替 + 号的字符串连接

4、不需要继承的类,尽量指定它的 final 修饰符

5、不需要外部访问类的方法和属性,尽量指定它的 private 修饰符

6、尽量重用已经创建的对象,例如可以通过缓存对象来重用
创建一个对象并不是 new 一下那么简单,它要耗费 CPU 和内存资源,特别是继承比较深或者属性复杂的类。

7、尽量降低同步带来的开销,例如使用同步块代替同步函数并且使用不同的锁
同步操作需要增加相关的虚拟机指令,不仅增加类文件的大小而且执行的指令比其它指令更耗资源。
如果是 JDK 5.0 ,同步操作尽量使用 java.util.concurrent 提供的技术,例如 Executors、CopyOnWriteArrayList、ConcurrentMap、ReentrantLock、AtomicInteger 等等。
//
private List<Listener> listeners = new ArrayList<Listener>();
public boolean addListener(Listener listener) {
	synchronized (listeners) {
	return listeners.add(listener);
	}
}
private List<Listener> listeners = new CopyOnWriteArrayList <Listener>();
public boolean addListener(Listener listener) {
	return listeners.add(listener);
}
//
privateMap<String, Object> map = new HashMap<String, Object>();
public Object getObj(String key) {
	synchronized (map) {
	Object bean = map.get(key);
	if (bean == null) {
		map.put(key, createObj());
		bean = map.get(key);
	}
	return bean;
	}
}
private ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getObj(String key) {
	Object bean = map.get(key);
	if (bean == null) {
		map.putIfAbsent(key, createObj());
		bean = map.get(key);
	}
return bean;
}
//
private final ReentrantLock lock = new ReentrantLock();
public void m() { 
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
}
//
private volatile int count = 0;
public synchronized void increment() {
	count++;
}
private AtomicInteger count = new AtomicInteger();
public void increment() {
	count.incrementAndGet();
}


8、尽量使用栈变量(方法的本地变量)和基本类型
public int add(int i) {
	int j = i ;  //使用本地变量
	for(int k=0; k < 10000; k++) {
		i += 1;
	}
	i = j;
	return i;
}


2
0
分享到:
评论

相关推荐

    Java程序性能优化读书笔记脑图

    Java程序性能优化 笔记 脑图

    java代码优化笔记

    在进行Java代码优化时,有多个方面需要考虑,本文档提供的是一系列详细的优化建议,涵盖了异常处理、资源管理、数据结构使用、性能提升等多个角度。首先,异常处理是代码优化的重要环节。不应该对所有异常都使用通用...

    狂神说JAVA的所有笔记.zip

    同时,可能会涵盖JVM(Java虚拟机)的工作原理,包括内存管理、类加载机制、性能优化等方面,这对于理解和优化JAVA程序的运行效率极其重要。 最后,笔记还可能包含了实际项目开发中的经验分享,如MVC框架Spring的...

    java学习笔记markdown

    这些基础知识是理解和编写任何Java程序的基石。 2. **面向对象编程**:深入讲解Java的面向对象特性,包括类、对象、封装、继承、多态等概念。这部分内容是Java的核心,有助于理解如何构建可维护和可扩展的软件系统...

    韩顺平编写的java学习笔记(全)

    - **Java程序运行**: Java程序需要在Java虚拟机(JVM)上运行。这意味着只要系统安装了合适的JVM,Java程序就可以在这个平台上运行。 #### 开发流程简述 1. **编写源文件**:使用`.java`扩展名保存源代码。 2. **...

    java笔记 java笔记

    Java的设计理念是“一次编写,到处运行”,这意味着编写的Java程序可以在任何安装了Java虚拟机(JVM)的设备上运行。 #### 1.1 Java的特点 - **面向对象**:Java是一种纯粹的面向对象的语言,几乎所有的数据都是以...

    java全套培训笔记

    笔记可能包含JUnit的使用,它是Java的单元测试框架,以及如何使用IDE(如Eclipse、IntelliJ IDEA)进行调试和性能优化。 最后,笔记可能还会介绍如何进行有效的代码管理和版本控制,如Git的使用,以及持续集成/持续...

    Java 学习笔记Java学习笔记

    8. 数据库操作:Java JDBC(Java Database Connectivity)允许Java程序与各种数据库交互。它提供DriverManager、Connection、Statement、PreparedStatement和ResultSet等接口和类。 9. Swing GUI:Swing是Java的...

    李兴华java笔记

    8. **JVM原理**:理解JVM(Java虚拟机)的工作原理有助于优化程序性能。笔记会涉及类加载机制、内存模型、垃圾回收等JVM核心概念。 9. **设计模式**:笔记会介绍常见的23种设计模式,如单例模式、工厂模式、装饰器...

    张龙JAVA圣思园笔记

    JVM是Java程序的运行环境,理解其工作原理对于优化代码至关重要。笔记可能涵盖了JVM内存模型(堆、栈、方法区、本地方法栈、程序计数器),垃圾收集(GC)机制,类加载过程,以及如何通过JVM参数进行性能调优。 ...

    Java基础尚硅谷宋红康学习笔记

    【Java基础】 Java是一种广泛使用的面向对象...这些是Java基础知识和高级技术的主要组成部分,掌握它们能够帮助开发者构建稳定、高效、可维护的Java应用程序。通过深入学习和实践,你可以不断提升自己的Java编程能力。

    阿里巴巴Java性能调优实战(2021-2022华山版)+Java架构核心宝典+性能优化手册100技巧.rar

    但是在项目的开始阶段,没有必要过早地介入性能优化,只需在编码的时候保证其优秀、高效,以及良好的程序设计。 在完成项目后,就可以进行系统测试了,可以将以下性能指标,作为性能调优的标准,响应时间、吞吐量、...

    韩顺平老师java视频课堂笔记

    3. **异常处理**:Java异常处理机制是程序健壮性的关键,笔记中会有异常的分类、抛出和捕获机制,以及自定义异常的实践示例。 4. **集合框架**:Java集合框架是存储和管理对象的重要工具,包括List、Set、Map等接口...

    Java语言基础笔记

    例如,每个Java程序都从`public class`开始,这是程序执行的入口点。 接着,我们探讨Java的数据类型。Java有两大类数据类型:基本类型(如int、float、boolean)和引用类型(如类、接口和数组)。基本类型直接存储...

    java精华学习笔记

    - **流程控制语句**: if条件语句、for循环、while循环和switch-case语句用于控制程序的执行流程。 2. **面向对象编程** - **类与对象**: 类是创建对象的模板,对象则是类的实例。理解类的定义、属性(字段)和...

    Java学习笔记(源码)

    13. **JVM原理**:理解Java虚拟机的工作方式,包括类加载、内存管理和垃圾回收机制,有助于优化程序性能。 14. **Java 8及更高版本的新特性**:例如lambda表达式、Stream API、Optional类等,这些新特性使Java编程...

    java校招学习笔记

    1. **Java语法**:变量、数据类型(基本类型和引用类型)、运算符、流程控制(条件语句和循环语句)、方法、类和对象的概念。 2. **面向对象编程**:封装、继承、多态三大特性,以及抽象类、接口和实现的深入理解。...

    JAVA学习笔记 林信良

    书中还详细阐述了异常处理,这是Java程序健壮性的重要组成部分。读者将学习如何使用try-catch-finally结构捕获和处理异常,以及如何自定义异常。 此外,《JAVA学习笔记》还涵盖了集合框架,包括ArrayList、...

    java超强学习笔记

    这部分是学习任何编程语言的起点,对于理解Java程序的运行机制至关重要。 2. **面向对象编程**:Java的核心特性之一就是面向对象,笔记可能会讲解类、对象、封装、继承、多态等概念,以及如何通过这些概念来设计和...

    Java随堂笔记

    这份“Java随堂笔记”正是为了帮助初学者以及希望巩固Java知识的开发者而编写的。笔记内容涵盖了Java的基础到进阶知识,特别是那些在企业开发中不可或缺的部分。 1. **Java基础** - **数据类型**:包括基本数据...

Global site tag (gtag.js) - Google Analytics