JVM内存增强之逃逸分析

作者:loading_... 时间:2022-01-23 00:22:08 

概念

逃逸分析一种数据分析算法,基于此算法可以有效减少 Java 对象在堆内存中的分配。 Hotspot 虚拟机的编译器能够分析出一个新对象的引用范围,然后决定是否要将这个对象分配到堆上.

 当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。

 当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。

//对象发生了逃逸,不会在栈上分配,有可能导致GC STW
public StringBuffer append(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
}
//对象未发生逃逸
public String append(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}

建议:开发中能在方法内部应用对象的,就尽量控制在内部

逃逸分析参数设计

在 JDK 1.7 版本之后, HotSpot 中默认就已经开启了逃逸分析,如果使用的是较早的

版本,开发人员则可以通过:

✓ 选项“ -XX:+DoEscapeAnalysis" 显式开启逃逸分析。

✓ 通过选项“ -XX:+PrintEscapeAnalysis" 查看逃逸分析的筛选结果。

使用逃逸分析

编译器可以对代码做如下优化

1.栈上分配:将堆分配转化为栈分配。如果一个对象在方法内创建,要使指向该对象的引用不会发生逃逸,对象可能是栈上分配的候选

/**
* 栈上分配测试(-XX:+DoEscapeAnalysis)
* -Xmx128m -Xms128m -XX:+DoEscapeAnalysis -XX:+PrintGC
*/
public class ObjectStackAllocationTests {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
alloc();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为: " + (end - start) + " ms");
// 为了方便查看堆内存中对象个数,线程 sleep
TimeUnit.MINUTES.sleep(5);
}
private static void alloc() {
byte[] data = new byte[10];//未发生逃逸
}
}

2.同步锁消除:

我们知道线程同步是靠牺牲性能来保证数据的正确性,这个过程的代价会非常高。程序 的并发行和性能都会降低。JVM 的 JIT 编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程应用?假如是,那么 JIT 编译器在编译这个同步块的时候就会取消对这部分代码上加的锁。这个取消同步的过程就叫同步省略,也叫锁消除

public class SynchronizedLockTest {
public void lock() {
Object obj= new Object();
synchronized(obj) {
System.out.println(obj);
}
}

3.标量替换分析

所谓的标量(scalar)一般指的是一个无法再分解成更小数据的数据。例如,Java 中 的原始数据类型就是标量。相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java 中的对象就是聚合量,因为他可以分解成其他聚合量和标量。在 JIT 阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过 JIT 优化,就会把这个对象分解成若干个变量来代替。这个过程就是标量替换。

public class ObjectScalarReplaceTests {
public static void main(String args[]) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
alloc();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为: " + (end - start) + " ms");
}
private static void alloc() {
Point point = new Point(1,2);
}
static class Point {
private int x;
private int y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
}
//对于上面代码,假如开启了标量替换,那么 alloc 方法的内容就会变为如下形式
private static void alloc() {
    int x=10;
    int y=20;
}

alloc 方法内部的 Point 对象是一个聚合量,这个聚合量经过逃逸分析后,发现他并没有逃逸,就被替换成两个标量了。那么标量替换有什么好处呢?可以大大减少堆内存的占用。因为一旦不需要创建对象了,那么就不再需要分配堆内存了。标量替换为栈上分配 提供了很好的基础。 

FAQ

1.什么是逃逸分析?

可以有效减少 Java 对象在堆内存中的分配压力和同步负载的算法

2.逃逸分析有什么优势、劣势?

逃逸分析是需要消耗一定的性能去执行分析的,所以说如果方法中的对象全都是处于逃逸状态,那么就没有起到优化的作用,从而就白白损失了这部分的性能消耗

来源:https://blog.csdn.net/gtxy_2015/article/details/126902967

标签:JVM,逃逸,分析
0
投稿

猜你喜欢

  • C#实现文件筛选读取并翻译的自动化工具

    2021-11-07 18:49:46
  • C# WebApi 异常处理解决方案

    2021-06-14 16:52:08
  • C#找不到类型名"SqlConnection"的有效解决方法

    2022-05-10 09:47:26
  • Android实现屏幕锁定源码详解

    2022-10-08 00:46:38
  • Android四大组件之Activity详解

    2022-08-22 00:00:49
  • Java三目运算中隐藏的自动拆装箱

    2023-11-29 10:47:10
  • UnityShader3实现彩光效果

    2021-09-02 03:48:53
  • SpringMVC使用ResponseEntity实现文件上传下载

    2023-08-20 02:10:58
  • C#导入导出Excel数据的两种方法

    2021-07-01 02:29:54
  • JAVA实现扫描线算法(超详细)

    2023-06-30 13:33:20
  • 深入解析Java中的Classloader的运行机制

    2023-07-16 11:47:59
  • java 定时同步数据的任务优化

    2021-09-18 19:48:22
  • C#实现餐厅管理系统

    2023-11-27 16:05:05
  • Android自定义View实现投票进度条

    2022-04-02 13:57:21
  • 详解Java多线程编程中CountDownLatch阻塞线程的方法

    2021-11-25 20:04:54
  • C#实现QQ聊天窗口

    2023-07-06 02:43:56
  • Android最简单的限制输入方法(只包含数字、字母和符号)

    2022-01-22 00:15:45
  • SpringBoot整合WebService服务的实现代码

    2021-11-11 14:59:11
  • java反射深入剖析(推荐)

    2022-10-10 18:50:35
  • 浅析Java中comparator接口与Comparable接口的区别

    2023-11-01 20:31:14
  • asp之家 软件编程 m.aspxhome.com