3分钟搞清楚 JVM逃逸分析
作为一个合格java开发者都知道,基本上所有对象都是在堆上创建。但是,这里还是没有把话说绝对哈,指的是基本上所有。
昨天一位朋友在面试中,就说了所有对象都在堆中创建,然后背面试官一阵的嘲笑。
开始我们的正文,我们今天来聊聊关于逃逸分析。
逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术。这是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。
逃逸分析的基本原理是:分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种称为方法逃逸;甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸;从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。
开启逃逸分析,编译器可以对代码进行如下优化:
同步消除:如果一个对象被逃逸分析发现只能被一个线程所访问,那对于这个对象的操作可以不同步。 栈上分配:如果确定一个对象不会逃逸出线程之外,那让这个对象在栈上分配内存将会是一个很不错的主意,对象所占用的内存空间就可以随栈帧出栈而销毁。 标量替换:如果一个对象被逃逸分析发现不会被外部方法访问,并且这个对象可以拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个比这个方法使用的成员变量来代替。将对象拆分后,可以让对象的成员变量在栈上分配和读写。
JVM中通过如下参数可以指定是否开启逃逸分析:
-XX:+DoEscapeAnalysis
:表示开启逃逸分析(JDK 1.7之后默认开启)。-XX:+DoEscapeAnalysis
:表示开启逃逸分析(JDK 1.7之后默认开启)。
-XX:-DoEscapeAnalysis
-XX:-DoEscapeAnalysis
:表示关闭逃逸分析。🎜同步消除
线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,无法被其他线程访问,那么这个变量的读写肯定就不会有竞争,对这个变量实施的同步措施也就可以安全地消除掉。
如以下代码:
public void method() { Object o = new Object(); synchronized (o) { System.out.println(o); } }
对对象o
加锁,但是对象o的生命周期与方法method()一样,所以不会被其他线程访问到,不会发生线程安全问题,那么在JIT编译阶段会被优化为如下所示:
public void method() { Object o = new Object(); System.out.println(o); }
这也被称为锁消除。
栈上分配
在Java虚拟机中,Java堆上分配创建对象的内存空间几乎是Java程序员都知道的常识,Java堆中的对象对于各个线程都是共享和可见的,只要持有这个对象的引用,就可以访问到堆中存储的对象数据。虚拟机的垃圾收集子系统会回收堆中不再使用的对象,但回收动作无论是标记筛选出可回收对象,还是回收和整理内存,都需要耗费大量资源。但是,存在一种特殊情况,如果逃逸分析确认对象不会逃逸出线程之外,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。
如以下代码:
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1000000; i++) { alloc(); } Thread.sleep(100000); } private static void alloc() { User user = new User(); }
代码很简单,就是循环创建100万次,使用alloc()方法创建100万个User对象。这里的alloc()方法中定义了User对象并没有被其他方法引用,所以符合栈上分配的要求。
JVM参数如下:
-Xmx2G -Xms2G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
启动程序,通过jmap工具查看实例数:
jmap -histo pid num #instances #bytes class name ---------------------------------------------- 1: 3771 2198552 [B 2: 10617 1722664 [C 3: 104057 1664912 com.miracle.current.lock.StackAllocationTest$User
我们可以看到程序总共创建了104057个User对象,远小于100万。我们可以关闭逃逸分析再来看下:
-Xmx2G -Xms2G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
启动程序,通过jmap工具查看实例数:
jmap -histo 42928 num #instances #bytes class name ---------------------------------------------- 1: 628 22299176 [I 2: 1000000 16000000 com.miracle.current.lock.StackAllocationTest$User
可以看到,关闭逃逸分析后总共创建了100万个User对象。对比来看,栈上分配对堆内存消耗,GC都有着重要的作用。
标量替换
若一个数据已经无法再分解成更小的数据来表示了,Java虚拟机中的原始数据类型(int 、long 等数值类型及reference类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。相对的,如果一个数据可以继续分解,那它就被称为聚合量(Aggregate),Java中的对象就是典型的聚合量。
假如逃逸分析能够证明一个对象不会被方法外部访问,并且这个对象可以被拆散,那么程序真正执行的时候将可能不去创建这个对象,而改为直接创建它的若干个被这个方法使用的成员变量来代替。
有如下代码:
public static void main(String[] args) { method(); } private static void method() { User user = new User(25); System.out.println(user.age); } private static class User { private int age; public User(int age) { this.age = age; } }
在method()
方法中创建User对象,指定age为25,这里User不会被其他方法引用,也就是说它不会逃逸出方法,并且User是可以拆解为标量的。所以alloc()
代码会优化为如下:
private static void alloc() { int age = 25; System.out.println(age); }
总结
尽管目前逃逸分析技术仍在发展之中,未完全成熟,但它是即时编译器优化技术的一个重要前进方向,在日后的Java虚拟机中,逃逸分析技术肯定会支撑起一系列更实用、有效的优化技术。
以上是3分钟搞清楚 JVM逃逸分析的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

该项目为了方便开发者更快监控多个远程主机jvm,如果你的项目是Spring boot那么很方便集成,jar包引入即可,不是Spring boot也不用气馁,你可以快速自行初始化一个Spirng boot程序引入jar包即可

通过JVM命令行参数,您可以细粒度地调整JVM行为。其中通用参数包括:设置Java堆大小(-Xms、-Xmx)设置新生代大小(-Xmn)启用并行垃圾收集器(-XX:+UseParallelGC)减少Survivor区内存占用(-XX:-ReduceSurvivorSetInMemory)消除冗余垃圾回收(-XX:-EliminateRedundantGCs)打印垃圾回收信息(-XX:+PrintGC)使用G1垃圾收集器(-XX:-UseG1GC)设置最大垃圾回收暂停时间(-XX:MaxGCPau

掌握JVM内存使用情况的要点与注意事项JVM(JavaVirtualMachine)是Java应用程序运行的环境,其中最为重要的就是JVM的内存管理。合理地管理JVM内存不仅可以提高应用程序的性能,还可以避免内存泄漏和内存溢出等问题。本文将介绍JVM内存使用的要点和注意事项,并提供一些具体的代码示例。JVM内存分区JVM内存主要分为以下几个区域:堆(He

Java是一种流行的编程语言,在开发Java应用程序的过程中,可能会遇到JVM内存溢出错误。这种错误通常会导致应用程序崩溃,影响用户体验。本文将探讨JVM内存溢出错误的原因和如何处理和避免这种错误。JVM内存溢出错误是什么?Java虚拟机(JVM)是Java应用程序的运行环境。在JVM中,内存被分为多个区域,其中包括堆、方法区、栈等。堆是用于存储创建的对象的

JVM虚拟机的作用及原理解析简介:JVM(JavaVirtualMachine)虚拟机是Java编程语言的核心组成部分之一,它是Java的最大卖点之一。JVM的作用是将Java源代码编译成字节码,并负责执行这些字节码。本文将介绍JVM的作用及其工作原理,并提供一些代码示例以帮助读者更好地理解。作用:JVM的主要作用是解决了不同平台上Java程序的可移

JVM内存参数设置:如何合理调整堆内存大小?在Java应用程序中,JVM是负责管理内存的关键组件。其中,堆内存是用于存储对象实例的地方,堆内存的大小设置对应用程序的性能和稳定性有着重要影响。本文将介绍如何合理调整堆内存大小的方法,并附带具体代码示例。首先,我们需要了解一些关于JVM内存的基础知识。JVM的内存分成了几个区域,包括堆内存、栈内存、方法区等。其中

在编写java程序来检查JVM是32位还是64位之前,我们先讨论一下JVM。JVM是java虚拟机,负责执行字节码。它是Java运行时环境(JRE)的一部分。我们都知道java是平台无关的,但是JVM是平台相关的。我们需要为每个操作系统提供单独的JVM。如果我们有任何java源代码的字节码,由于JVM,我们可以轻松地在任何平台上运行它。java文件执行的整个过程如下-首先,我们保存扩展名为.java的java源代码,编译器将其转换为扩展名为.class的字节码。这发生在编译时。现在,在运行时,J

JVM原理详解:深入探究Java虚拟机的工作原理,需要具体代码示例一、引言随着Java编程语言的迅猛发展和广泛应用,Java虚拟机(JavaVirtualMachine,简称JVM)也成为了软件开发中不可或缺的一部分。JVM作为Java程序的运行环境,能够提供跨平台的特性,使得Java程序能够在不同的操作系统上运行。在本文中,我们将深入探究JVM的工作原
