Java对象逃逸及逃逸分析

这篇具有很好参考价值的文章主要介绍了Java对象逃逸及逃逸分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章目录

前言

一、对象逃逸是什么?

1.1 概念

1.2 代码分析

二、逃逸分析(一种分析算法)

1.什么是逃逸分析

2.代码优化实践

2.2 同步锁消除

2.3 标量替换分析

总结


前言

随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。

逃逸分析技术到现在还不是很成熟,虽然经过逃逸分析可以做标量替换、栈上分配、锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。

一、对象逃逸是什么?

1.1 概念

对象逃逸是指当我们在某个方法里创建了一个对象,这个对象除了被这个方法引用,还在方法体之外被其它的变量引用。这样的后果是当这个方法执行完毕后,GC无法回收这个对象,就被称为对象逃逸了。逃逸的对象的内存在堆中,未逃逸的对象内存分配在栈中。

1.2 代码分析

public stringBuffer append(String apple,String pear){
   StringBuffer buffer=new StringBuffer();
   buffer.append(apple);
   buffer.append(pear);
   return buffer;
}

这种写法直接返回的是对象,用处就是被别的变量所引用,会造成对象逃逸,从而增加了GC的压力,引发STW(stop the world)现象,不推荐这样写。我们可以做修改如下代码:

public string append(String apple,String pear){
   StringBuffer buffer=new StringBuffer();
   buffer.append(apple);
   buffer.append(pear);
   return buffer.toString();
}

这种写法就避免了对象逃逸,从而减小了GC的压力。下面这种写法就是在方法内创建了一个对象,没有被外界方法所引用,称为未逃逸对象。

public void create(int x,int y) { 
    Point p1= new Point(x,y); 
    //…     
    p1=null; 
    
} 

二、逃逸分析(一种分析算法)

1.什么是逃逸分析

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

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

2.代码优化实践

使用逃逸分析,编译器可以对代码做如下优化:

  • 栈上分配:将堆分配转化为栈分配。如果一个对象在方法内创建,要使指向该对象的引用不会发生逃逸,对象可能是栈上分配的候选。
  • 同步省略:又称之为同步锁消除,如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
  • 分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 寄存器中。

2.1栈上分配

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];//未发生逃逸
    }
}

这里我们在运行时配置一下属性,设置了内存大小、逃逸分析和记录GC:

第一步,在main方法运行绿色小三角点击右键,选择最后一个按钮。

Java对象逃逸及逃逸分析

第二步,添加配置属性 -Xmx128m -Xms128m -XX:+DoEscapeAnalysis -XX:+PrintGC 

Java对象逃逸及逃逸分析

我们来看一下运行效果:

Java对象逃逸及逃逸分析

 我们会发现花费时间为9ms且没有调用GC,如果把配置属性的逃逸分析去掉再来看运行结果:

Java对象逃逸及逃逸分析

这时会发现花费的时间更多了,且调用了GC。

2.2 同步锁消除

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

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

2.3 标量替换分析

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

我们在2.1栈上分配的基础上更改一下配置:-XX:+EliminateAllocations 

public class ObjectStackAllocationTests {
    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;
        }
    }

Java对象逃逸及逃逸分析

Java对象逃逸及逃逸分析

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

总结

本次介绍了对象逃逸的概念以及对象逃逸案例分析和代码优化,小伙伴们学会了吗?为了提高我们代码的质量,一定要规范写代码哦!文章来源地址https://www.toymoban.com/news/detail-445599.html

到了这里,关于Java对象逃逸及逃逸分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM 常量池、即时编译与解析器、逃逸分析

    常量池底层使用HashTable key 是字符串和长度生成的hashValue,然后再hash生成index, 该index就是key;Value是一个HashTableEntry; 1、key     hashValue = hash string(name, len)     index = hash to index(hashValue);     1、根据字符串(即 name)以及字符串的长度计算出hashValue     2、根据hashValue计算出

    2024年02月11日
    浏览(51)
  • Docker Dirtypipe(CVE-2022-0847)漏洞复现与分析容器逃逸

    同脏牛,通过写只读内存,对映射的内存做篡改 GitHub - greenhandatsjtu/CVE-2022-0847-Container-Escape: CVE-2022-0847 used to achieve container escape 利用CVE-2022-0847 (Dirty Pipe) 实现容器逃逸 云原生之容器安全实践-安全客 - 安全资讯平台 (anquanke.com) 从脏管道(CVE-2022-0847)到docker逃逸 - 先知社区 (aliy

    2024年02月13日
    浏览(46)
  • Java利用JOL工具分析对象分布

    Markword:存储对象自身运行时数据如hashcode、gc分代年龄等,64位系统总共占用8个字节,关于Markword详细内存分布如下 类型指针:对象指向类元数据地址的指针,jdk8默认开启指针压缩,64位系统占4个字节 数组长度:若对象不是数组,则不分配空间大小,若是数组,则为4个字节长

    2024年02月07日
    浏览(26)
  • Java设计模式-前言

     馆长准备了很多学习资料,其中包含 java方面,jvm调优,spring / spring boot /spring cloud ,微服务,分布式,前端,js书籍资料,视频资料,以及各类常用软件工具,破解工具  等资源。请关注“IT技术馆”公众号,进行关注,馆长会每天更新资源和更新技术文章等。请大家多多关

    2024年01月21日
    浏览(41)
  • FlinkCDC 菜鸟教程-文章目录

    背景篇 环境篇  准备一台已经安装了 Docker 的 Linux 或者 MacOS 电脑。 准备教程所需要的组件 版本对应关系 安装 环境检查 工具篇 flink kibana 概念篇 Docker 介 绍 Docker Compose 介 绍 Kibana介 绍 实践篇 演示: Mysql CDC 导入 Elasticsearch 启动服务 准备数据 在 Flink SQL CLI 中使用 Flink DDL 创建

    2024年02月09日
    浏览(48)
  • Linux 学习目录合集【文章索引】

    前言:本内容为笔者自学笔记内容。 本文中的操作环境:腾讯云服务器:CentOS 7.6 64bit 学习阶段规划: Linux 基本操作【基本命令、vim、makefile使用等】 Linux 系统【进程:概念、控制、通信;IO基础;多线程等】 Linux 网络【网络基础、套接字编程、IO高级等】 学习集: C++ 入门

    2024年02月07日
    浏览(46)
  • Java面向对象之抽象类、类、接口分析参考

    Java 抽象类 抽象类不能实例化对象,所以抽象类必须被继承,才能被使用; Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口 1. 抽象方法: 用Abstract 声明抽象方法,只包含一个方法名,没有方法体。方法名后面直接跟一

    2024年02月10日
    浏览(43)
  • 【数据结构】【王道】【数据结构实现】文章目录

    持续更新中。。。 数据结构 链接 顺序表实现及基本操作(可直接运行) 文章链接 无头结点单链表的实现及基本操作(可直接运行) 文章链接 带头结点单链表的实现及基本操作(可直接运行) 文章链接 双链表的实现及基本操作(可直接运行) 文章链接 循环链表的实现及

    2023年04月08日
    浏览(90)
  • 【Java基础教程】(七)面向对象篇 · 第一讲:上干货!面向对象的特性、类与对象、内存结构引用分析、垃圾收集器 GC处理、封装性详解、构造方法、匿名对象、简单 Java 类~

    程序是将数据和逻辑封装在一起的代码段。在Java中,方法是常用的代码段封装方式。然而,在Java中,方法必须存在于一个类中才能使用。因此,我们将进入本章的核心内容——面向对象编程。 利用面向对象设计的程序可以实现代码的重用,并方便开发者进行项目维护。面向

    2024年02月13日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包