Java性能优化

目录

基准测试和JIT

GC相关

堆内存

原生内存

线程

JavaEE相关

Java SE 相关


 

基准测试和JIT

即便是单线程的基准测试用例,变量也要申明成 volatile类型的,这样可以防止编译器做优化

要加入一定的热身期

监控命令
jcmd 打印java进程所涉及的基本类,线程和VM信息
jconsole
jhat,读取内存堆转储,并有助于分析
jmap,提供堆转储和其他JVM内存使用的信息
jinfo,查看JVM的系统属性,可以动态设置一些系统属性
jstack
jstat
jvisualvm

比如
jcmd [process_id] VM.system_properties
或者 jinfo -sysprops [process_id]
jcmd [process_id] VM.version
jcmd [process_id] VM.command_line
jcmd [process_id] VM.flags [-all]
打印所有的调优标志
java other_options -XX:+PrintFlagsFinal -version

jstack [process_id]
或者 jcmd [process_id] Thread.print

关闭某个属性
jinfo -flag -PrintGCDetails [process_id]
可以在运行期间设置属性开启/关闭,但不是所有属性都支持热关闭/开启的

Java Mission Control 商业监控工具,其关键特性是java飞行记录器(Java Flight Recorder)
 

 


GC相关

GC输出

-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimestamps
-XX:+PrintGCDateStamps
-Xloggc:filename
-XX:+UseGCLogfileRotation
-XX:+NumberOfGCLogfiles

 

Througput GC算法是一种空间和时间的权衡
开始时可以用默认的自适应方式
如果把暂停时间设置的太短,就会触发更频繁的GC,会导致吞吐量下降
如果把GC比率设置太低,也会导致堆空间变小,更可能会频繁触发GC
有两个相关的参数
-XX:MaxGCPauseMillis=N
-XX:GCTimeRatio=N

Through收集器
分为Minor GC,Full GC
暂停时间越短GC就会越多,GC次数减少意味着时间增大堆空间增加
对through调优是一种取舍

CM收集器
初始标记,并发标记,预清理,重新标记,并发清理,(清理失败)
增量式CMS垃圾收集器,在单CPU上会增加CPU负担,增量收集采用间歇性的方式收集,1.8之后被废弃

G1收集器
初始标记,并发标记,预清理,重新标记,并发标记
分成2048个region
新生代+老年代 混合收集模式
并发模式失败(类似CMS),疏散失败(Srvivor和老年代没有空间容纳幸存对象)
调整后台收集线程数
调整垃圾收集器运行频率(默认占到45%时回收)-XX:InitiatingHeapOccupancyPercent=N
调整混合垃圾收集周期
 分区默认为35%时启动收集 -XX:G1MixedGCLiveThresholdPercent
 最大混合式GC周期数,可解决晋升失败问题,但会增加GC周期停顿时间,-XX:G1MixedGCCountTarget=N
 最大停顿时间 -XX:MaxGCPauseMillis

晋升调整
-XX:MinSurvivorRatio=N
-XX:InitialTenuringThreshold=N
-XX:TargetSurvivorRatio
-XX:+AlwaysTenure
-XX:+NeverTenure

分配大页
-XX:+UseTLAB
-XX:TLABWasteTargetPercent=N
-XX:TLABWasteIncrement=N
-XX:G1HeapRegionSize=N

 

堆内存

内存溢出错误
1.原生内存不足
 Exception in thread "main" java.lang.OutOfMemoryError
 unable to create new native thread

2.永久代或元空间内存不足
  Exception in thread "main" java.lang.OutOfMemroyError: PermGen space

3.堆内存不足
  Exception in thread "main" java.lang.OutOfMemoryError: java heap space

4.达到GC的开销限制
  Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

-XX:+UseGCOverheadLimit 如果超过了这个设定值,比如默认98%的时间用在GC上
-XX:+GCHeapFreeLimit    如果释放的内存少于这个指标,默认是2%,释放的内存不足堆的2%

减少内存使用
减少对象大小
延迟对象初始化
不可变对象
字符串保留 intern()

对象重用
1.线程池
2.JDBC池
3.EJB池
4.大数组
5.原生NIO缓冲区
6.安全相关类
7.字符串编解码器对象
8.StringBuilder协助者
9.从NDS查询到的名字
10.ZIP编解码器

弱引用,软引用,幻引用
Finalizer

 

原生内存

内存占用最小化
1.堆
2.线程栈
3.代码缓存
4.直接字节缓冲区

原生内存跟踪
jcmd [process_id] VM.native_memory summary
-XX:NativeMemoryTracking=off|summary|detail

原生NIO缓存
-XX:MaxDirectMemorySize

大页 
+XX:+UseLargePages
+XX:LargePageSizeInBytes

指针压缩
+XX:+UseCompressedOops

jcmd 11674 VM.native_memory summary
11674:

Native Memory Tracking:

Total: reserved=1573497KB, committed=36193KB
-                 Java Heap (reserved=253952KB, committed=16384KB)
                            (mmap: reserved=253952KB, committed=16384KB) 
 
-                     Class (reserved=1056875KB, committed=4971KB)
                            (classes #389)
                            (malloc=107KB #122) 
                            (mmap: reserved=1056768KB, committed=4864KB) 
 
-                    Thread (reserved=10304KB, committed=10304KB)
                            (thread #11)
                            (stack: reserved=10260KB, committed=10260KB)
                            (malloc=33KB #52) 
                            (arena=12KB #20)
 
-                      Code (reserved=249631KB, committed=2567KB)
                            (malloc=31KB #295) 
                            (mmap: reserved=249600KB, committed=2536KB) 
 
-                        GC (reserved=839KB, committed=71KB)
                            (malloc=7KB #79) 
                            (mmap: reserved=832KB, committed=64KB) 
 
-                  Compiler (reserved=132KB, committed=132KB)
                            (malloc=1KB #22) 
                            (arena=131KB #3)
 
-                  Internal (reserved=203KB, committed=203KB)
                            (malloc=171KB #1206) 
                            (mmap: reserved=32KB, committed=32KB) 
 
-                    Symbol (reserved=1356KB, committed=1356KB)
                            (malloc=900KB #64) 
                            (arena=456KB #1)
 
-    Native Memory Tracking (reserved=32KB, committed=32KB)
                            (malloc=2KB #29) 
                            (tracking overhead=30KB)
 
-               Arena Chunk (reserved=171KB, committed=171KB)
                            (malloc=171KB) 
 

 

线程

线程池ThreadPoolExecutor设置
1.最小任务数
2.最大任务数
3.任务队列,有界队列,无界队列

ForkJoinPool
使用map-reduce原理
如果整个待执行的任务是均衡的用ThreadPoolExecutor更好
否则用ForkJoinPool更好
窃取双端队列

同步的方式
volatile
CAS
原生同步
竞争不大用CAS,竞争比较多用原生同步

Amdahl定律
volatile导致的伪共享会让性能急剧下降
偏向所,优先处理访问频繁的线程
自旋锁,JDK8废弃

 

 

JavaEE相关

减少输出,包括减少空格
合并CSS和JavaScript
压缩输出
不要使用JSP动态编译

减少会话中保留的数据,减少会话时间
servlet可能有专门的线程池,EJB也有专门的线程池,同样JMS也是

EJB bean的执行过程
1.创建EJB
2.处理注解并且将依赖的资源注入这个新的EJB对象
3.调用注解为@PostConstruct的函数
4.如果是有状态bean,调用注解为@Init的函数或者ejbCreate()函数
5.执行业务方法
6.调用任何注解为@PreRemove的函数
7.如果是有状态bean,则调用remove()函数
EJB池化后,只需要执行业务方法,其他初始化销毁的都可以不做
有状态bean的钝化(Passivation),尽量避免这么做

及时在一个服务器中,本地接口也比远程接口快,因为少了序列化/反序列化步骤
所有远程EJB都必须支持IIOP(CORBA)协议
Web Service使用JAX-WS
RESTful使用JAX-RS
XML和Json的解析方式,XML的验证会影响很大性能
创建JAXB对象比创建DOM对象昂贵,但JAXB写XML速度要快于DOM对象
Serializable标记接口
Externalizable自定义序列化接口(包含readExternal和writeExternal)

序   号

区   别

Serializable

Externalizable

1

实现复杂度

实现简单,Java对其

有内建支持

实现复杂,

由开发人员自己完成

2

执行效率

所有对象由Java统一保存,

性能较低

开发人员决定哪个对象保存,

可能造成速度提升

3

保存信息

保存时占用空间大

部分存储,

可能造成空间减少

在一般的开发中,因为Serializable接口的使用较为方便,所以出现较多


JDBC驱动程序类型1-4,四种
PrepareStatment可以重用sql语句,比Statement性能更好
预处理语句必须依靠单个连接进行池化,而预处理语句会消耗大量堆空间
数据库查询返回的取舍,频繁的调用数据库每次取一小部分,或者一次取很多但会造成GC

Container Mnaged Transactions CMT 使用容器管理事务
User Managed Transaction UMT  使用用户管理事务
XA事务
减少写入的字段,不必要跟新的不写
延迟载入大型字段
不太容易冲突的使用乐观锁,经常发生冲突的使用悲观锁
加入JPA缓存

 


Java SE 相关

I/O缓冲区,socket I/O也需要缓冲
ClassLoader的双亲委派模型,多线程创建类时候会有影响性能,1.7之后改为并行模式性能提升
ClassLoader使用loadClass()寻找类,如果找不到调用findClass(),我们自己的类加载只需实现findClass即可
-XX:+AlwaysLockClassLoader 还原为JDK6的持有锁的类加载模式

Random 线程安全的
ThreadLocalRandom 实现方式类似Random,一种伪随机数
SecureRandom,获得了更多的随机数实现确保更安全,如果无法获取更多随机数可能会等待好几秒

现在的JNI未必比Java代码更快了,若一定要使用应限制Java到C的调用次数,这会影响性能
现在的JVM处理异常速度也很快了,但获取异常栈信息(尤其栈很深)会损耗性能
防御性代码比抛出异常更好
可以用 -XX:-StrackTraceInThrowable 禁止生成异常栈信息(不推荐)
String str = "aa"+"bb"+"cc" 编译器会做优化
str += "aa";
str += "bb";
会变成 
str = new StringBuilder(str).append("aa");
str = new StringBuilder(str).append("bb");

Java集合类
线程安全的集合
设定集合的大小,IO类中的ByteArrayOutuptStream也是类似的
ConcurrentHashMap
AggressiveIpts 会在啊一些基本的类中开启某些优化,导致一些副作用
AutoBoxCacheMax 自己装箱cache

lambda性能并不差,可能比匿名类性能更好
应该从方便编程的角度出发,因为性能上没什么差别

list.stream().filter(s->s.charAt(0)!='A').(s->s.charAt(1)!='A').(s->s.charAt(2)!='A').(s->s.charAt(3)!='A')


过滤器会先判断最后一个
(s->s.charAt(3)!='A')
拿到数据之后交给倒数第二个,然后倒数第三个。。。
过滤器的性能比迭代器好,因为能少处理很多数据
但多个过滤器会有开销,实现时需要注意

 

 

Java性能优化_第1张图片

 

 

参考

G1垃圾收集器入门-原创译文

详解 JVM Garbage First(G1) 垃圾收集器

 

你可能感兴趣的:(编程语言)