Java程序优化知识笔记

2017-03-30 From 程序之心 By 丁仪

迁移 64 位虚拟机未必性能更好

业务量上升以后,需要使用的内存随之增加,而在通常 32 位系统上,单个进程占用的最大内存通常是 2GB,且考虑到堆外内存的使用,32 位机器可能无法满足内存要求,一种常见的应对方式就是换用 64 位服务器。而对于 Java,由于指针膨胀和字节对齐,同一个程序在 64 位虚拟机上占用的内存会多于 32 位虚拟机。开发者换用 64 位虚拟机后,很可能会增加虚拟机的堆大小,而这将导致 Full GC 垃圾回收的时间大大增加,导出堆快照也变得困难。

因此,使用 64 位虚拟机增加内存时,需要特别注意对内存的使用,尽量不要触发 Full GC 导致长时间停顿。另一种方法是建立 32 位虚拟机的集群来提高性能。

堆外内存溢出

Java 本地方法调用,如调用 C++ 实现的本地模块、NIO 的 DirectByteBuffer,都会占用大量内存。而在开发中,开发者往往重点关注了堆内存的大小,在内存溢出时也倾向于增加堆内存,而忽视了堆外内存的使用。堆外内存并不会像堆内存一样不足马上通知 GC 进行垃圾回收,堆外内存只能等待老年代空间不足进行 Full GC 时顺便回收内存,否则堆外内存只能等到空间不足时抛出内存溢出异常,然后请求 GC 进行回收。

因此,配置虚拟机,除考虑常规的堆大小外,优化时还需要考虑 Direct Memory、线程栈、socket 缓冲区、JNI 代码、虚拟机、GC 占用的内存大小。

外部命令的时间消耗

Java 开发中如果需要执行 shell 脚本,可以使用 Runtime.getRuntime().exec 方法,还能从返回的 Process 对象中读取标准输出、错误输出、等待执行结束。根据方法注释,该方法首先复制当前进程产生一个子进程,在子进程中执行命令,结束后退出子进程。

进程的复制比较消耗 CPU 和内存,应尽量通过 Java 程序本身去完成相关功能。

多线程使用线程池

Java 虚拟机中没有给用户用的多进程方法,并行处理更多地使用多线程方式。默认情况下,Linux 限制用户的线程数量上限为 1024,当然包括了系统中运行的所有线程。通常情况下,线程资源不会被耗尽,但多线程程序如果频繁创建新线程也会遇到线程资源不足的情况。一方面,可以调整系统设置,提高线程数上限,另一方面,应尽量避免频繁创建线程。线程虽小,创建时一样要消耗时间和内存。

多线程程序应尽量采用 Java 的线程池,这样线程的个数总体可控,使用时可以避免创建线程的时间消耗。Java 提供了多种功能强大的线程池类型,基于线程池可以对任务进行缓存、按照一定的时间频率执行任务、返回执行结果、分叉与合并等。

本文来源:程序之心,转载请注明出处!

君子曰:学不可以已。
《黑匣子思维》
“黑匣子思维”是一种记录和审视失败并从中吸取经验的积极态度。不惧怕面对失败,反而视失败为学习的途径。不会否认过失、推诿责任和想方设法脱身,而会把失败作为样本深入研究。 缺乏从失败中学习的态度、勇气和能力,会对个体或行业带来严重危害。千方百计避免犯错并不是我们的目标,学习如何聪明而有意义地犯错,将每一次失败作为测试我们成绩的机会。
发表感想

© 2016 - 2024 chengxuzhixin.com All Rights Reserved.

浙ICP备2021034854号-1    浙公网安备 33011002016107号