性能优化 提升效率 资源优化配置 硬件性能恒定 jvm性能调优
OOM CPU GC频繁
jvm性能优化 自身策略 编译器优化 运行期优化
方法内联
//方法内联
public class MethodInline {
//方法内联优化前
private int inlineBefore(int x1, int x2, int x3, int x4) {
return add(x1,x2) + add(x3, x4) ;
}
private int add(int x1, int x2){ return x1 + x2;}
// 方法内联优化后
private int inlineAfter(int x1, int x2, int x3, int x4){
return x1+x2+x3+x4;
}
}
方法 函数
限制
热点 <325字节
非热点 <35字节
java 多态 无法确定方法的唯一性 static private final
逃逸分析
对象是否被 外部访问,不会逃逸的对象 可以将该对象在栈帧上分配内存
方法中的对象大多是朝生夕死
demo1
class A {
public static B b;
public void globalVariablePointerEscape() { // 给全局变量赋值,发生逃逸
b = new B();
}
public B methodPointerEscape() { // 方法返回值,发生逃逸
return new B();
}
public void instancePassPointerEscape() {
methodPointerEscape().printClassName(this); // 实例引用传递,发生逃逸
}
}
class B {
public void printClassName(A a) {
System.out.println(a.class.getName());
}
}
demo2
public class EscapeAnalysis {
//方法中定义的对象被外部所引用 对象逃逸了
public Worker getWork0bject() {
Worker work = new Worker();
work.setId(1);
work.setUsername("yzt");
work.setPassword("123456");
return work;
}
//方法中的对象没有被外部所引用 对象没逃逸
public void setWorkObject() {
Worker work = new Worker();
work.setId(1);
work.setUsername("yzt");
work.setPassword("123456");
}
}
当你判断出你的对象不发生逃逸的时候 我们可以做 一些优化
1 栈上分配
2 同步消除 当我发现某个对象只能从一个线程访问 那么这个对象上的操作不需要同步
3 标量替换
优化 (栈上分配)
逃逸分析优化 – 栈上分配
优化原理:分析找到未逃逸的变量,将变量类的实例化内存直接在栈里分配(无需进入堆),分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量也被回收。
这是优化后的处理方式,对比可以看出,主要区别在栈空间直接作为临时对象的存储介质。从而减少了临时对象在堆内的分配数量。
逃逸分析另一个重要的优化 – 锁省略
如果通过逃逸分析能够判断出指向某个局部变量的多个引用被限制在同一方法体内,并且所有这些引用都不能“逃逸”到这个方法体以外的地方,那么HotSpot会要求JIT执行一项优化动作 – 将局部变量上拥有的锁省略掉。
这就是锁省略(lock elision)。
//栈上分配
public class Demo3 {
public static void main(String[] args) {
for (int i = 0; i < 5000000; i++) {
create0bject();
}
}
public static void create0bject() {
new Object();
}
}
class DoubleSlot {
final int value1;
final int value2;
public DoubleSlot(int value1, int value2) {
this.value1 = value1;
this.value2 = value2;
}
}
static int slotValue(DoubleSlot slot) {
return slot.value1 + slot.value2;
}
static int sum(int[] values) {
int sum = 0;
int length = values.length;
for(int i=1; i<100; i++)
sum(values);
for(int i=0; i<100; i++)
test(values);
}
2.优化方式
2.1 栈上分配(Stack Allocation)
在Java虚拟机中,对象是创建在Java堆中,这是每一位有经验Java开发人员在熟悉不过的常识。Java堆中的对象是对于各个线程都是共享和可见的,主要持有这个对象的引用,就可以轻松访问存储在对象的数据。虚拟机的垃圾收集器也可以回收不再使用的对象,但是回收动作无论是筛选可回收对象,还是回收和整理内存都需要消耗时间和性能。如果确定一个对象不会逃逸出方法之外,那让这个对象在栈上分配内存将是一个大胆而又很不错的注意。大家都知道,JVM虚拟机中80-90%的对象都是朝生夕死的,如果对象所占用的内存空间随着栈帧出栈而销毁,那垃圾收集器的压力就会大大减小。
2.2 同步消除(Synchronization Elimination)
对于多线程的应用而言,我们通过同步代码块实现线程的安全。然而线程同步代码块也是一个相当耗费性能的过程。如果通过逃逸分析确定这个对象不会逃逸出线程,即无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,这样,我们就可以大胆的把同步线程代码块消除。
2.3 标量替换(Scalar Replacement)
如果我问一下什么是对象,大家一定会想到封装继承多态。然而大家想过没有,所谓的对象,也可以理解成最终由一个或多个原始数据类型(int,long等)组成。这里的对象可以认为是聚合量,原始数据类型就是所谓的标量。如果逃逸分析证明一个对象不会被外部访问,并且这个对象呗拆散的话,那程序真正执行的时候将可能不创建这个对象,而改为直接创建他的若干个呗这个方法使用到的成员变量带代替。将对象拆分后,除了可以让对象的成员变量在栈上分配和读写之外,还可以为后续进一步的优化手段创建条件。
3.权衡分析
逃逸分析是一个相当复杂的过程,那么逃逸分析所要消耗的性能与逃逸分析多带来的性能提升究竟哪个大,还是一个未知事项。如果经过逃逸分析后发现没有几个不逃逸的对象,那简直就是得不偿失了。所以java虚拟机只能对于时间压力相对较小的算法来完成逃逸分析。
4.参数设置
手动开启逃逸分析:-XX:+PrintEscapeAnaysis
开启标量替换:-XX:EliminateAllocations
开启同步消除:+XX:+EliminateLocks
标量替换情况:-XX:+PrintEliminateAllocations
jvm在编译阶段引入了JIT(即时编译) 技术,而随着这种技术的成熟,栈上分配、标量替换优化技术也产生了一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了,
在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。
-XX:+PrintGC -Xms5M -Xmn5M -XX:+DoEscapeAnalysis
2 同步消除 当我发现某个对象只能从一个线程访问 那么这个对象上的操作不需要同步
//同步锁消除
public class Demo4 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 5000000; i++) {
create0bject();
}
System.out.println("cost =" + (System.currentTimeMillis() - start) + "ms");
}
public static void create0bject() {
synchronized (new Object()) {
}
}
}
3 0标量替换
标量 不可被进一步分解的量 java的基本数据类型 int long
聚合量 能进一步分解的量 对象
面试 性能调优
OOM
1 内存分配的维度 大并发 提前分析 提前处理 限流 客户端开始就进行一层一层的服务驱动
2 内存泄漏维度 一点点最终导致内存泄漏 原因 内存溢出是结果
@RestController
public class TLController {
@GetMapping(value ="/t1")
public String tl(HttpServletRequest request){
ThreadLocal<Byte[]> tl = new ThreadLocal<Byte[]>();
tl.set(new Byte[1024 * 1024]) ;
return "ok";
}
}
运行jmeter压测
解决:内存泄漏导致的内存溢出 导致cpu使用过高
nginx
sential hsytrsix 资源
sentinel是方法资源层面
一个转发限流,一个服务限流
GC频率
CPU使用率过高
1死锁
jstack Jstack PID
//运行主类
public class DeadLockDemo {
public static void main(String[] args) {
Thread t1 = new Thread(
new DeadLock(true));
Thread t2 = new Thread(
new DeadLock(false));
t1.start();
t2.start();
}
}
class DeadLock implements Runnable{
boolean lockFormer;
static Object o1 = new Object();
static Object o2 = new Object();
DeadLock(boolean lockFormer){
this.lockFormer = lockFormer;
}
@Override
public void run() {
if(this.lockFormer){
synchronized (o1) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1ok");
}
}
}else{
synchronized (o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("1ok");
}
}
}
}
}
使用jps、 jsatck、 jmap -heap(快照)查看
C:\Users\86130>jps
18960
16040 Jps
17896 KotlinCompileDaemon
20008 RemoteMavenServer
6248 Launcher
2764 DeadLockDemo
C:\Users\86130>jstack 2764
2021-10-31 22:34:58
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b15 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x00000000032a3000 nid=0x5bb4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001eeb9000 nid=0xc70 waiting for monitor entry [0x00000000202bf000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLock.run(DeadLockDemo.java:43)
- waiting to lock <0x000000076c6b37a0> (a java.lang.Object)
- locked <0x000000076c6b37b0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001ee70000 nid=0x2c24 waiting for monitor entry [0x00000000201bf000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLock.run(DeadLockDemo.java:32)
- waiting to lock <0x000000076c6b37b0> (a java.lang.Object)
- locked <0x000000076c6b37a0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001edb2800 nid=0x4944 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001ecf7800 nid=0x4ee8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001ecf5000 nid=0x48ac waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001ecf2000 nid=0x1e84 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001ecc0000 nid=0x43ac waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001ec8c000 nid=0x5958 runnable [0x000000001f2be000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076c7c5320> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076c7c5320> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001eb50000 nid=0x3d14 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001eb4f800 nid=0x25cc runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001eae0800 nid=0x5654 in Object.wait() [0x000000001efbe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076c508ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076c508ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000003398000 nid=0x17c8 in Object.wait() [0x000000001eabe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076c506b50> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076c506b50> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001cbda000 nid=0x177c runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000032b8800 nid=0x5aac runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000032ba000 nid=0x3b48 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000032bc000 nid=0x37d4 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000032bd800 nid=0x3ebc runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00000000032c0800 nid=0x4d00 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00000000032c2000 nid=0x4f58 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00000000032c5000 nid=0x4f80 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00000000032c6000 nid=0x3d20 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001ee41000 nid=0x5b6c waiting on condition
JNI global references: 33
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001cbe3728 (object 0x000000076c6b37a0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001cbe2288 (object 0x000000076c6b37b0, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at DeadLock.run(DeadLockDemo.java:43)
- waiting to lock <0x000000076c6b37a0> (a java.lang.Object)
- locked <0x000000076c6b37b0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at DeadLock.run(DeadLockDemo.java:32)
- waiting to lock <0x000000076c6b37b0> (a java.lang.Object)
- locked <0x000000076c6b37a0> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
C:\Users\86130>jmap -heap 2764
Attaching to process ID 2764, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b15
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4213178368 (4018.0MB)
NewSize = 88080384 (84.0MB)
MaxNewSize = 1404043264 (1339.0MB)
OldSize = 176160768 (168.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 66060288 (63.0MB)
used = 7927936 (7.5606689453125MB)
free = 58132352 (55.4393310546875MB)
12.00106181795635% used
From Space:
capacity = 11010048 (10.5MB)
used = 0 (0.0MB)
free = 11010048 (10.5MB)
0.0% used
To Space:
capacity = 11010048 (10.5MB)
used = 0 (0.0MB)
free = 11010048 (10.5MB)
0.0% used
PS Old Generation
capacity = 176160768 (168.0MB)
used = 0 (0.0MB)
free = 176160768 (168.0MB)
0.0% used
3154 interned Strings occupying 258640 bytes.
大并发的情况下
30指的是库存支付业务链路的长度以及链路过程中经过的对象
8g
几百 os
几百namespace
日志收集其他探针程序
最多5G 5000m
新生代 1666(16660.8(from to占0.2)/36 抗的时间=37秒young gc)
对象会晋升 old gc-》full gc
解决
1 加机器
2 扩大新生代大小 300008/36=66.6超过1分钟
3 扩大整个内存
老年代 3334
每一个请求会产生一个订单 一个订单就会生成一个order对象(1个order对象1kb顶天了)
G1