JVM中如何实现标量替换的详细机制?

2026-05-16 03:291阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1443个文字,预计阅读时间需要6分钟。

JVM中如何实现标量替换的详细机制?

概述:通常在Java中创建对象,大家都会认为是在堆中创建。但自JDK 6开始,有逃逸分析等优化技术,可以使得对象在栈上分配,从而不再绝对在堆中创建。

通常在Java中创建对象,大家都会认为是在堆上分配的。但在JDK 6以后,随着逃逸分析等技术的应用,可以在栈上分配对象,从而不必一定在堆上创建。下面简要介绍逃逸分析。

逃逸分析:逃逸分析是一种编译时优化技术,通过分析对象的使用情况,确定对象是否被其他线程访问。如果对象仅被当前线程访问,且不会被传递到其他线程,那么可以确定对象不会逃逸到堆外,就可以在栈上分配。

标准替换技术:逃逸分析通过以下技术实现对象的栈上分配:

1.逃逸分析:分析对象使用情况,确定对象是否逃逸。

2.标量替换:将对象替换为基本类型或基本类型数组。

3.引用传播:通过改变对象的引用来优化对象的创建。

4.方法内联:将方法内的代码内联到调用处,减少对象的创建。

关于逃逸分析:

逃逸分析通过分析对象的引用关系,确定对象是否会在堆外访问。如果对象不逃逸,就可以在栈上分配,减少内存使用和提高性能。

概述

通常在java中创建一个对象,大家都认为是在堆中创建。 在jdk6开始有逃逸分析,标量替换等技术,关于在堆中创建对象不再绝对。

关于标量替换,通过以下几点进行概述:

  1. 逃逸分析
  2. 标量替换是什么
  3. 测试标量替换

逃逸分析

逃逸分析是一种分析技术,分析对象的动态作用域,供其他优化措施提供依据。比如分析一个对象不会逃逸到方法之外或线程之外,其它优化措施(栈上分配,标量替换等)根据逃逸程度进行优化。

逃逸分析示例

public class EscapeAnalysis { public Person p; /** * 发生逃逸,对象被返回到方法作用域以外,被方法外部,线程外部都可以访问 */ public void escape(){ p = new Person(26, "TomCoding escape"); } /** * 不会逃逸,对象在方法内部 */ public String noEscape(){ Person person = new Person(26, "TomCoding noEscape"); return person.name; } } static class Person { public int age; public String name; ... // 省略构造方法 }

标量替换是什么

标量可以理解成一种不可分解的变量,如java内部的基本数据类型、引用类型等。 与之对应的聚合量是可以被拆解的,如对象。
当通过逃逸分析一个对象只会作用于方法内部,虚拟机可以通过使用标量替换来进行优化。
比如上述noEscape()方法中person对象只会在方法内部,通过标量替换技术得到如下伪码:

/** * 不会逃逸,对象在方法内部 */ public String noEscape(){ int age = 26; String name = "TomCoding noEscape"; return name; }

测试标量替换

接下来我们通过对noEscape()方法进行测试,主要测试两种场景:

  1. 不使用标量替换
  2. 使用标量替换

以下测试是在jdk8中运行(注jdk8默认是开启逃逸分析,标量替换技术的)

测试代码如下:

void testEliminateAllocationsWithNoEscape() { int n = 100000000; long start = System.currentTimeMillis(); EscapeAnalysis escapeAnalysis = new EscapeAnalysis(); for (int i = 0; i < n; i++) { // noEscape()不会发生逃逸 escapeAnalysis.noEscape(); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); }

  • 不使用标量替换

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:-EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:3006毫秒,gc发生2000多次。

  • 使用标量替换

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:+EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:20毫秒,gc发生6次。

再来看看发生逃逸的对象使用标量替换效果

测试代码如下:

void testEliminateAllocationsWithEscape() { int n = 100000000; long start = System.currentTimeMillis(); EscapeAnalysis escapeAnalysis = new EscapeAnalysis(); for (int i = 0; i < n; i++) { // escape()发生逃逸 escapeAnalysis.escape(); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); }

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:+EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:3705毫秒,gc发生2000多次。

总结

  1. 可以看到通过逃逸分析与标量替换技术有效的减少了gc次数(减少了对象在堆中创建的数量)。
  2. 实际编码过程中避免对象逃逸情况是一种理想的情况。可以形成一种编码意识,尽量去减少对象逃逸。

思考

标量替换只是利用逃逸分析其中的一种优化措施, 还有其它优化措施吗?

JVM中如何实现标量替换的详细机制?

以上就是详解jvm中的标量替换的详细内容,更多关于jvm 标量替换的资料请关注易盾网络其它相关文章!

本文共计1443个文字,预计阅读时间需要6分钟。

JVM中如何实现标量替换的详细机制?

概述:通常在Java中创建对象,大家都会认为是在堆中创建。但自JDK 6开始,有逃逸分析等优化技术,可以使得对象在栈上分配,从而不再绝对在堆中创建。

通常在Java中创建对象,大家都会认为是在堆上分配的。但在JDK 6以后,随着逃逸分析等技术的应用,可以在栈上分配对象,从而不必一定在堆上创建。下面简要介绍逃逸分析。

逃逸分析:逃逸分析是一种编译时优化技术,通过分析对象的使用情况,确定对象是否被其他线程访问。如果对象仅被当前线程访问,且不会被传递到其他线程,那么可以确定对象不会逃逸到堆外,就可以在栈上分配。

标准替换技术:逃逸分析通过以下技术实现对象的栈上分配:

1.逃逸分析:分析对象使用情况,确定对象是否逃逸。

2.标量替换:将对象替换为基本类型或基本类型数组。

3.引用传播:通过改变对象的引用来优化对象的创建。

4.方法内联:将方法内的代码内联到调用处,减少对象的创建。

关于逃逸分析:

逃逸分析通过分析对象的引用关系,确定对象是否会在堆外访问。如果对象不逃逸,就可以在栈上分配,减少内存使用和提高性能。

概述

通常在java中创建一个对象,大家都认为是在堆中创建。 在jdk6开始有逃逸分析,标量替换等技术,关于在堆中创建对象不再绝对。

关于标量替换,通过以下几点进行概述:

  1. 逃逸分析
  2. 标量替换是什么
  3. 测试标量替换

逃逸分析

逃逸分析是一种分析技术,分析对象的动态作用域,供其他优化措施提供依据。比如分析一个对象不会逃逸到方法之外或线程之外,其它优化措施(栈上分配,标量替换等)根据逃逸程度进行优化。

逃逸分析示例

public class EscapeAnalysis { public Person p; /** * 发生逃逸,对象被返回到方法作用域以外,被方法外部,线程外部都可以访问 */ public void escape(){ p = new Person(26, "TomCoding escape"); } /** * 不会逃逸,对象在方法内部 */ public String noEscape(){ Person person = new Person(26, "TomCoding noEscape"); return person.name; } } static class Person { public int age; public String name; ... // 省略构造方法 }

标量替换是什么

标量可以理解成一种不可分解的变量,如java内部的基本数据类型、引用类型等。 与之对应的聚合量是可以被拆解的,如对象。
当通过逃逸分析一个对象只会作用于方法内部,虚拟机可以通过使用标量替换来进行优化。
比如上述noEscape()方法中person对象只会在方法内部,通过标量替换技术得到如下伪码:

/** * 不会逃逸,对象在方法内部 */ public String noEscape(){ int age = 26; String name = "TomCoding noEscape"; return name; }

测试标量替换

接下来我们通过对noEscape()方法进行测试,主要测试两种场景:

  1. 不使用标量替换
  2. 使用标量替换

以下测试是在jdk8中运行(注jdk8默认是开启逃逸分析,标量替换技术的)

测试代码如下:

void testEliminateAllocationsWithNoEscape() { int n = 100000000; long start = System.currentTimeMillis(); EscapeAnalysis escapeAnalysis = new EscapeAnalysis(); for (int i = 0; i < n; i++) { // noEscape()不会发生逃逸 escapeAnalysis.noEscape(); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); }

  • 不使用标量替换

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:-EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:3006毫秒,gc发生2000多次。

  • 使用标量替换

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:+EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:20毫秒,gc发生6次。

再来看看发生逃逸的对象使用标量替换效果

测试代码如下:

void testEliminateAllocationsWithEscape() { int n = 100000000; long start = System.currentTimeMillis(); EscapeAnalysis escapeAnalysis = new EscapeAnalysis(); for (int i = 0; i < n; i++) { // escape()发生逃逸 escapeAnalysis.escape(); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); }

将jvm参数设置如下:

-Xms5m 最小堆内存5M -Xmx5m 最大堆内存5M -XX:+PrintGC 打印gc日志 -XX:+EliminateAllocations 关闭标量替换优化

运行后在我本机的耗时:3705毫秒,gc发生2000多次。

总结

  1. 可以看到通过逃逸分析与标量替换技术有效的减少了gc次数(减少了对象在堆中创建的数量)。
  2. 实际编码过程中避免对象逃逸情况是一种理想的情况。可以形成一种编码意识,尽量去减少对象逃逸。

思考

标量替换只是利用逃逸分析其中的一种优化措施, 还有其它优化措施吗?

JVM中如何实现标量替换的详细机制?

以上就是详解jvm中的标量替换的详细内容,更多关于jvm 标量替换的资料请关注易盾网络其它相关文章!