java 1.6 性能_总结:一文带你了解JAVA的性能优化

本文探讨了JDK1.6前后substring方法的内存优化,强调了对象缓存、正则表达式使用及字符串操作的注意事项。介绍了JVM内存管理,包括堆内存分配、栈内存回收和线程栈的优化。此外,还分享了线程池大小的选取原则,以及如何避免死锁和提高并发性能。最后,提出了性能监控、日志优化、SQL调优和数据库架构设计等方面的实践策略。
摘要由CSDN通过智能技术生成

JDK1.6前后的subString方法实现

在JDK1.6 subString内部实现是子String仍然会保留父String的char数组引用,这在一定程度上会造成内存泄露,特别是当有特别长的字符串实际上只需要其特别短的字符串,但是由于引用依赖,GC无法回收,造成父String一直滞留在JVM内存中无法回收。

JDK1.6之后subString内部对其优化,调用拷贝父String的char数组中的子char数组形成一份新的副本,如此子String与父String之间不存在依赖关系,GC能够对不在使用的父String进行GC回收。

循环遍历中如果存在重复创建大量相同的字符串,建议创建缓存池进行对象缓存。

避免使用正则表达式,如必要使用至少要把Pattern进行缓存,避免反正创建Pattern编译。

当需要对一个基本数据类型进行字符串转换应该尽可能使用toString或者String.valueOf(obj) 替换 obj+""。

在进行大文本字符串拼接时,应该为StringBuffer,StringBuilder设置初始化容量值

***

JVM篇

将-Xms与-Xmx设置相同,避免每次垃圾回收完成后JVM重新分配内存。

原则上应该避免太大深度的递归,毕竟递归越深其中间栈帧所产生的数据引用仍然有效,无法被GC清除。

当虚拟机栈中需要存储基本数据或对象引用时需要调整-Xss来避免发生StackOverflowError异常

在虚拟机栈中随着栈帧的pop会对栈帧内的内存进行及时的清理,所以在局部方法内部中其中间变量应该尽可能使用8大基本数据类型才能够随着栈帧结束而立即内存回收。

为了避免频繁触发JVM对基本数据类型进行拆包与装包操作,其中间变量应该尽量使用基本数据类型而不是其包装类型。

JDK默认只缓存-128~+127的Integer和Long 如果超出该范围则会创建出新的对象,如果对计算数据敏感可是适当通过-XX:AutoBoxCacheMax增大缓存范围。

G1与CMS收集器的选择,G1收集器会对堆内内存进行划分成Region,其堆越小则划分的Region数也会越少,在小堆的表现并不会比CMS突出。笔者认为8G以下采用CMS比较合适。目前比较期待Java 11 新加入的ZGC号称可以达到10ms 以下的 GC 停顿。

通过-XX:+AlwaysPreTouch提前初始化好真正的物理内存,而不是需要才进行内存申请初始化。默认未添加该参数时而-Xms、-Xmx只是告诉告诉操作系统需要多少内存,从而避免被其他进程使用,而只有当正直使用时才会进行内存逐渐申请。比如在堆中Eden区进行对象创建又或者Young区转Old区的内存空间。不过该参数也会影响启动时长,随着堆内存越大启动时间也会增大。

JDK监控工具 Jconsole,jProfile,VisualVM 可以通过可视化界面查看JAVA堆内存使用情况进行判断是否需要进行代码优化。

***

线程篇

如何为线程池选择合适线程数?

线程任务一般可以分为计算密集型和IO密集型。

计算密集型CPU处于忙碌中,此时需要做内存数据读写计算,没有任务的阻塞状态。而IO密集型任务,在执行IO时堵塞,CPU处于等待状态,等待过程中系统会将时间片分给其他线程处理。

计算密集型任务线程数最好与系统核心数挂钩,毕竟4核单线程主机在某一时刻只能同时跑4个线程,如果过多的线程数反而会因为切换上下文而耗费更多的任务时间,可以通过调用JDK自带的方法Runtime.availableProcessors获得系统支持的可以核心数。N+1。

对于IO密集型,合适的线程数可以获得良好的性能支持,系统通过将IO阻塞的任务线程的时间片交给未被阻塞的任务线程,通过合适的调度发挥出比单线程更好的性能支持。在选择线程数应该考虑内存支持程度,避免过多的线程数导致内存激增产生OOM。2N+1。

线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

一个完整的系统不应只有一个线程池,应该对线程任务进行梳理分类,划分出各自的任务类型以及工作负载来提供多个线程池。

如果需要加锁竟可能使用加锁代码块将锁范围控制在最小范围中,而不是在方法体中加锁。

尽量避免嵌套取锁,容易造成死锁问题。

合理时候使用读写锁来替换synchronized独占锁。

JDK1.8流处理对于大量需要计算的数据时可采用parallelStream进行并发数据处理,可提高处理速度。

多了解各类对象对并发支持性例如HashMap、SimpleDateFormat等。

合理使用ThreadLocal来实现数据在线程本地化。

如果对执行过程没有严格的串行顺序可以采用FutureTask对计算结果统一取值拼装。

***

杂谈篇

为避免循环之间切换,尽量采用小循环嵌套大循环。

避免在循环中大概率抛出异常的代码块进行TryCatch,尽可能放在循环外层TryCatch。

代码块中应尽量使用懒加载,即需要时才进行加载,不需要则不加载。

在JVM级别做适当的缓存级别,可以使用EhCache、Guava Cache。Ehcache适合支持持久化功能,有集群解决方案,而Guava Cache只是一个支持LRU的concurrentHashMap,没有Ehcache那么多特性,只支持增删改查,刷新规则和时效规则设定等最基本的设定。

设置日志输出级别,避免大量无关紧要的日志输出,影响业务系统性能。

SQL调优可以通过索引分析,减少查询字段,限制表读数据等进行分析调优。

数据库瓶颈可以通过读写分离减少单服务器压力,以及通过垂直拆分或水平拆分将数据表数据合理治理。

引入缓存架构减轻数据库压力。

哪些是性能瓶颈的关键点

有些任务需要大量的计算,需要不停地占用CPU资源,导致其他任务抢占CPU的能力变弱而导致响应速度下降,而带来的性能问题。比如任务内过多的重复计算,无限的自循环计算,JVM频繁的FULL GC,多线程做大量的上下文切换等。

一般来说说内存的读写速度非常快,一般不存在性能问题,但是由于内存成本比硬盘高即内存空间是有限的,应该注意内存的范围避免应内存耗尽而导致OOM等问题。

磁盘IO是引起系统性能的一大因素。涉及到数据落地等问题应尽量使用硬盘顺序读写而不是随机读写,顺序读写的读写能力比随机读写能力强大太多。

数据库方面除了必要的SQL优化减少查询时间外如有必要需要引入缓存层减轻数据库压力

合理使用锁减少并发时造成性能损耗

防止瞬间时抛出大量的异常,抛出异常会不停从堆栈内拔取异常信息,非常耗性能。

衡量性能的指标

系统响应时间

从发送请求到接收到数据时所需要的时间

系统吞吐量

单位时间内成功地传送数据的数量大小

负载承受能力

随着并发量的增加最终导致系统抛出大量的异常,整个系统处于不可用的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值