开发高性能JAVA应用程序基础(内存篇)
虽然Java的垃圾回收和当前高配置的服务器可以让程序员大部分时间忘掉OutOfMemoryError的存在,但是访问量增大后频繁的GC会额外消耗CPU (使用top查看结果为us值高),系统响应速度下降,积压的请求又会占用更多内存从而恶性循环,严重时可能导致系统不断Full GC造成应用停顿。
优化内存的使用可从以下几方面着手:
一、节流
1 使用单例模式
单例模式是开发者最早接触并使用的设计模式之一,尽管写代码的时候可能还不知道用了设计模式。简单来说就是构造函数private化,通过静态方法获得唯一实例。因为其特性,对于某些场景例如每次请求都要使用无状态工具类的检验方法,使用单例模式可以大量节省创建新对象的开销。
- public class Singleton {
- private Singleton() {}
- private static Singleton instance = new Singleton();
- public static Singleton getInstance() {
- return instance;
- }
- public void doSomething() { }
- }
2 缓存常用对象
简单来说就是按一定特征创建"对象缓存池",使用集合类保存已创建的对象,当有相同特征的对象申请时,使用缓存池中现有的对象代替通过 new关键字重新创建。
- public class BigObjectPoolTest {
- public static void main(String[] args) {
- long start = System.nanoTime();
- for(int i = 0; i < 10000; i++) {
- BigObjectPool.getBigObject("xxx", true);
- }
- System.out.println("使用缓存池耗时" + TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS) + "毫秒");
- start = System.nanoTime();
- for(int i = 0; i < 10000; i++) {
- BigObjectPool.getBigObject("xxx", false);
- }
- System.out.println("不使用缓存池耗时" + TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS) + "毫秒");
- }
- }
- class BigObjectPool {
- private static Map<String, BigObject> map = new HashMap<String, BigObject>();
- static {
- map.put("xxx", new BigObject("xxx"));
- map.put("yyy", new BigObject("yyy"));
- }
- public static BigObject getBigObject(String key, boolean usePool) {
- if(usePool) {
- BigObject bo = map.get(key);
- if(bo == null) {
- bo = new BigObject(key);
- }
- return bo;
- } else {
- return new BigObject(key);
- }
- }
- }
- class BigObject{
- private String name;
- private byte[] data = new byte[1024 * 1024];
- public BigObject(String name) { this.name = name; }
- }
以-Xms32m -Xmx32m -Xloggc:d:/gc.log 参数运行
- 使用缓存池耗时3毫秒
- 不使用缓存池耗时998毫秒
(查看gc.log,可以观察到不使用缓存池触发Minor GC 1000次以上)
实际业务中通常使用EhCache等框架代替自己实现缓存池。
与这种实现原理相似的也有一个设计模式:享元模式,区别是享元模式更关注类设计结构上的优化,对上下文环境的设计也做了明确定义。
3 避免设计过大的对象
如果业务模型中要求的类的属性和方法都非常多,可以尝试将其拆分成多个小类,再通过合成/聚合模式组装成一个大类,这也符合设计模式的优化思想。甚至可以结合上面的对象缓存池的方式将其中一部分内容缓存化。
- class Composition {
- private BigObject bigObject = null;
- private int id;
- public void setBigObject(BigObject bigObject) {
- this.bigObject = bigObject;
- }
- public Composition(int id) {
- this.id = id;
- }
- }
- Composition c = new Composition(1);
- c.setBigObject(BigObjectPool.getBigObject("xxx", true));
4 一些小技巧
使用StringBuilder代替用+号连接字符串
尽量使用int, long等基本类型代替Integer, Long包装对象
合理利用SoftReference和WeakReference
二、开源 - 调整虚拟机参数
一般设置 java -server -Xms2048m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=256m
-Xms和-Xmx决定java堆区可使用的内存最小值和最大值,通常设为相同的值,避免运行期间反复的重新申请内存。如果出现OutOfMemoryError: Javaheap space,则在硬件允许的情况下临时调大-Xmx,为排查问题和优化代码争取时间。
-XX:PermSize和-XX:MaxPermSize决定永久代可用空间大小,存放class和meta信息,通常设置为相同的值。如果出现OutOfMemoryError: PermGen space,说明加载的类和jar文件过多,可以调大这两个参数值。
如果web容器下有多个应用引用了相同的第三方jar文件,可以转移到容器的共享目录。
另一个重要参数是-Xmn,决定堆区新生代的大小,通常占-Xmx的比值设置为1/4到1/3。如果业务中有大量体积大且生命周期很短的对象创建需求,可适当调大新生代空间以利于失效对象在新生代中被回收。
此外,可通过参数设置回收算法:
–XX:+UseSerialGC
–XX:+UseParallelGC
–XX:+UseParallelOldGC
–XX:+UseConcMarkSweepGC
回收算法的选择和对比需要较大的篇幅介绍,这里不做详细的解释。通常来说,对于响应时间优先的web应用,ConcMarkSweepGC(CMS)是个不错的选择。
需要注意的是,经过几代发展后,JVM对内存管理已经做的非常好。如果不是有明确的证据证明JVM的默认选择不合理,就没必要做过多细节的调整设置。调整后,可通过-XX:+PrintGCDetails -XX:+PrintGCTimeStamps等参数输出GC信息进行比对,优化的首要目标是减少Full GC次数和时间。
参考资料: 分布式java应用基础与实践
开发高性能JAVA应用程序基础(内存篇)的更多相关文章
- 开发高性能JAVA应用程序基础(集合篇)
集合类在开发中使用非常频繁,使用时合理的选择对提高性能小有帮助.而且大部分面试都会有与集合相关的问题,例如ArrayList和LinkedList的对比. 了解API的集成与操作架构,才能了解何时该采 ...
- 高性能JAVA开发之内存管理
这几天在找一个程序的bug,主要是java虚拟机内存溢出的问题,调研了一些java内存管理的资料,现整理如下: 一.JVM中的对象生命周期 对象的生命周期一般分为7个阶段:创建阶段,应用阶段,不可视阶 ...
- 使用Java开发高性能网站需要关注的那些事儿
无论大型门户网站还是中小型垂直类型网站都会对稳定性.性能和可伸缩性有所追求.大型网站的技术经验分享值得我们去学习和借用,但落实到更具体的实践上并不是对所有网站可以适用,其他语言开发的网站我还不敢多说, ...
- Java开发高性能网站需要关注的事
转自:http://www.javabloger.com/java-development-concern-those-things/ 近期各家IT媒体举办的业内技术大会让很多网站都在披露自己的技术内 ...
- 使用Java开发高性能网站需要关注的那些事儿2
近期各家IT媒体举办的业内技术大会让很多网站都在披露自己的技术内幕与同行们分享,大到facebook,百度,小到刚起步的网站.facebook,百度之类的大型网站采用的技术和超凡的处理能力的确给人 ...
- Java进阶 JVM 内存与垃圾回收篇(一)
JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...
- 第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token
一.access_token简介 为了使第三方开发者能够为用户提供更多更有价值的个性化服务,微信公众平台 开放了许多接口,包括自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等, 开 ...
- 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备
相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ...
- 第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容
第一部分:微信JS-SDK介绍 微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...
随机推荐
- Bat脚本命令说明
命名参考 使用方式如果不知道如何使用就到cmd窗口键入help 命名名 例如:"help del" 命令名 注释 ASSOC 显示或修改文件扩展名关联. ATTRIB 显示或更改文 ...
- 让intellij idea 14 支持ES6语法
用eclipse做前端开发,用到了webpack,结果各种依赖导致软件卡的一比,简直不能动!虽然在同事的帮忙下,修改了一下配置,但仍然卡的没脾气.改用intellij idea 14解决了卡的问题,但 ...
- 小白的Python之路 if __name__ == '__main__' 解析
if __name__ == '__main__' 参考文献: http://www.cnblogs.com/xuxm2007/archive/2010/08/04/1792463.html http ...
- dataGrideView的使用
总的连接地址:http://group.cnblogs.com/topic/40730.html 微软解说:https://msdn.microsoft.com/zh-cn/library/syste ...
- Python函数篇(6)-常用模块及简单的案列
1.模块 函数的优点之一,就是可以使用函数将代码块与主程序分离,通过给函数指定一个描述性的名称,并将函数存储在被称为模块的独立文件中,再将模块导入主程序中,通过import语句允许在当前运行的程序 ...
- Python学习(三):迭代器、生成器、装饰器、递归、算法、正则
1.迭代器 迭代器是访问集合的一种方式,迭代对象从集合的第一个元素开始访问,直到元素被访问结束,迭代器只能往前不能后退,最大的优点是不要求事先准备好整个迭代过程中的元素,这个特点使得它特别适合用于遍历 ...
- SQL企业级面试题
链接:90root MySQL企业面试题 1. 开发有一堆数据插入,如何防止插入的中文数据产生乱码? 2. 如何批量更改数据库表的引擎,如:myisam改为innodb 3. 如何批量更改数据库字符集 ...
- asp.net core 2.0+sqlsugar搭建个人网站系列(0)
一些废话 马上就要过年了,回顾这一年最大的收获就是技术有了很大的提升,其他的方面没有什么改变,现在还是单身小屌丝一枚. 这一年来学习的主要重点就是asp.net core,中间也使用 core+EF做 ...
- qt中线程的使用方法
QT中使用线程可以提高工作效率. 要使用线程要经过一下四个步骤: (1)先创建一个c++ class文件,记得继承Thread,创建步骤如下: a.第一步 b.第二步 (2)自定义一个run函数,以后 ...
- 微信公众号开发——通过ffmpeg解决amr文件无法播放问题
今天刚好碰到个需求,要在微信浏览器中实现录音,并在其他页面上播放.录音功能本身是JS SDK的功能,倒没啥问题,然而录音的文件保存下来是amr格式,而IOS的浏览器没法播放amr(据说微信浏览器的vi ...