【 java 安全】Java对象都是堆上分配?看完Java中对象逃逸分析就知道答案了

news/2024/10/9 14:57:53/文章来源:https://www.cnblogs.com/o-O-oO/p/18410657

原创 龙虾编程

随着JIT编译期的发展与逃逸分析技术逐渐成熟,所有的对象都分配到堆上也渐渐变得不是一定的。在编译期间JIT会对代码做很多优化,其中有一部分优化是减少内存堆分配压力,这里有一种重要的技术叫逃逸分析。逃逸分析是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。

1、方法逃逸和未逃逸

逃逸分析是分析指针动态范围的方法,当对象在方法中分配后,其指针有可能被返回,此时就会被其他方法或者线程所引用到,我们称这种现象为指针 (引用)的逃逸。逃逸分析在jdk1.7之后是默认开启的,控制逃逸分析的参数是:-XX:+DoEscapeAnalysis

(1)方法逃逸

一个对象被创建(new)出来之后,如果是作为参数传递到外部了,那么这个对象被外部所调用,这就是方法逃逸。
如下所示的代码:

public static Dog  getDog(){Dog dog = new Dog();dog.setName("small dog");dog.setAge(2);return dog;
}

上面的代码中局部变量dog可能会被外部调用,这个就是方法的逃逸。

(2)未逃逸

与方法逃逸相对的就是方法未逃逸了,如下就是典型的未逃逸:

public static void getDog(){Dog dog = new Dog();dog.setName("small dog");dog.setAge(2);
}

此时的局部变量dog不会被外部引用,所以不会出现方法逃逸。

public static String getDogStr(){Dog dog = new Dog();dog.setName("small dog");dog.setAge(2);return dog.toString();
}

此时方法的返回值是一个字符串而不是一个对象的引用,所以这个局部变量dog也不会被外部锁引用,就不是方法逃逸。

2、线程逃逸

public static StringBuffer craeteStringBuffer(String s1, String s2) {StringBuffer sb = new StringBuffer();sb.append(s1);sb.append(s2);return sb;
}

sb是一个方法内部变量,在上述代码中直接将其返回出去,这个StringBuffer有可能被其他方法所改变,这样它的作用域就不只是在方法内部,也有可能被外部线程访问到。

典型的如类变量或可以在其他线程中访问的实例变量,可能别其他的而线程访问到,我们称之为线程逃逸。

方法逃逸和线程逃逸他们逃逸强度不一样,如下图所示:

3、逃逸分析意义

(1)对象在栈上分配

如果确定一个对象不会逃逸到线程之外,那么可以考虑将这个对象在栈上分配,对象占用的内存随着栈帧弹出而销毁,这样可以减轻垃圾回收的压力。
如以下图所示的:

Person对象在栈上分配了,此时栈帧3对应的方法调用结束后,就会弹出栈,那么Person对象占用的空间也就随之释放。

(2)同步锁消除

线程同步本身是一个相对耗时的过程,如果逃逸分析能够确定一个变量不会逃逸出线程,那么这个变量的读写肯定就不会有竞争,对这个变量添加的同步锁也就可以安全地消除掉,如下代码:

public void setDog(){Object lock = new Object();synchronized(lock){system.out.println("龙虾编程");}
}

上面的代码中对lock对象进行加锁,但是lock对象只在setDog()方法内部并且对象也不会被其他线程调用,所以在JIT编译阶段就会被优化这段代码,如下所示:

public void setDog(){Object lock = new Object();system.out.println("龙虾编程");
}

在synchronized中,如果JIT经过逃逸分析之后发现代码并没有线程安全问题,就会做锁消除。

(3)标量替换

int a;

这个变量a是基本数据类型,它是不可拆分,我们称之为标量。

public class Dog {private int age;private String name;private Origin orign;
}

相对的,我们还可以分解的对象数据叫做聚合量,Java中的对象就是聚合量(如上的Dog类对象),因为它可以分解成其他标量(age、name)和聚合量(orign)。

在 JIT 阶段如果经过逃逸分析发现一个对象不会被外界访问的话,那么经过JIT优化就会把这个对象拆解成包含若干个成员变量来代替,我们称这个过程为标量替换。
如下所示的标量替换过程的代码:

public static void main(String args[]){getDog();
}public static void getDog(){SmallDog smallDog = new SmallDog(1, "smallDog");system.out.println("name: " + smallDog.getName() + ", age: " + smallDog.getAge());
}public class SmallDog {private String name;private int age;public SmallDog(int age, String name){this.age = age;this.name = name;}
}

方法getDog()经过逃逸分析发现它中的smallDog对象不会被方法部访问并且这个对象可以被拆散,那么可以不创建对象,直接创建若干个成员变量代替,这样可以让对象的成员变量在栈上分配和读写。优化之后的代码如下所示:

public static void main(String args[]){getDog();
}public static void getDog(){//对象的变量替换String name = "smallDog";iint age = 1;system.out.println("name: " + name  + ", age: " + age);
}public class SmallDog {private String name;private int age;public SmallDog(int age, String name){this.age = age;this.name = name;}
}

标量替换可以很好的减少堆内存的占用,因为不需要创建对象,就不再需要分配堆内存了。
标量替换默认是没有开启的,如果需要开启标量替换就添加参数-XX:+EliminateAllocations

总结:

(1)逃逸分析可以带来一定程度上的性能优化,但是逃逸分析自身也需要经过一系列的复杂处理,所以需要消耗一定的时间。

(2)不是所有的对象都会在堆上分配空间,也有可能在栈上分配。当JVM通过逃逸分析发现一个对象只在一个方法中使用并且不会逃逸出这个方法,那么它可能会选择在栈上分配这个对象。如果一个对象可以被拆解为一些基本类型或引用类型的字段,并且这些字段都只在一个方法中使用,那么JVM可能会选择进行标量替换,将这个对象拆解并在栈上分配空间。

(3)加锁不一定会生效,因为JIT根据逃逸分析会将锁消除。

(4)逃逸分析分为不逃逸、方法逃逸、线程逃逸,逃逸的强度也是越来越强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/810538.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SQL SERVER】PIVOT与UNPIVOT之行列转换

基础例子 在数据处理的过程中,常常遇到行列转换的问题。例如,人员的考勤。可能表格中,1~12月都在同一个字段,实际中,为了查看方便,同一个人的考勤记录,能在同一行,这样查询起来比较方便(行转列)。或者,表格设计的时候就是1~12月,在其他数据分析时需要将列转行。即类…

SkyWalking组件自定义链路追踪

SkyWalking组件通过添加相关配置就可以获取到接口的相关信息,更加方便的追踪和处理问题 接下去讲下步骤: 1、在service层添加两个注解;@Trace@Tags({@Tag(key = "getDataByCode",value = "returnedObj"),@Tag(key = "getDataByCode",value = …

沈师傅食品携手纷享销客CRM系统,加速数字化转型

沈师傅食品有限公司是一家专业研发、生产和销售鸡蛋干系列产品的大型集团 公司,技术与研发实力雄厚,先后获得多项国家专利。公司成立于2006年,开创 了全新的鸡蛋干品类,创办人沈国平先生素有“鸡蛋干之父”之称,先后被央视、 四川电视台、北京卫视、优酷、凤凰网等国内知名…

总奖金高达10万元!华为算法精英实战营“亲和任务调度系统”来啦!

在无线领域,利用AI技术对任务准确建模、多核系统任务最优调度等问题都是非常有价值的算法难题。随着物联网、大数据、AI时代的到来,时延、可靠性等指标要求越来越高,海量的数据分析、大量复杂的运算对CPU的算力要求越来越高。CPU内部的大部分资源用于缓存和逻辑控制,适合运…

webapi 创建(空)

1. 打开vs2019 ,选择创建新项目2. 选择ASP.NET Web 应用程序(.NET Framework)3. 配置项目信息(名称,位置,框架)4. 选择空模板(WebAPI复选框选中)5. 这样里面就没有MVC的三层,因为前后端分离,webapi中只有两层。6. 空的WebApi程序创建完成。

高产胜那啥,带你上线我的新项目!

希望大家能通过这个项目掌握企业级项目的开发、优化和上线方法,得到全方面编程技能和程序员素养的提升。大家好,我是程序员鱼皮。9月,我处于极度爆肝状态,成功完结了最新带大家做的项目 面试刷题平台 。当我们做完一个项目后,一定要记得把项目上线,这样才算是完成了学习的…

创建空webapi服务

1. 打开vs2019 ,选择创建新项目2. 选择ASP.NET Web 应用程序(.NET Framework)3. 配置项目信息(名称,位置,框架)4. 选择空模板(WebAPI复选框选中)5. 这样里面就没有MVC的三层,因为前后端分离,webapi中只有两层。6. 空的WebApi程序创建完成。

.NET 代码混淆工具-JIEJIE.NETWX

阅读目录前言 项目介绍 项目功能 项目效果:蓝猫机场 项目地址 最后前言 JIEJIE.NET是一款强大的开源.NET程序集混淆工具。它利用深度加密技术和多样化的混淆策略,有效地保护了.NET软件的版权和源代码安全,防止未经授权的访问和篡改。 项目介绍 JIEJIE.NET是一个用C#开发的开源…

深入理解 HDFS 错误恢复

我们从动态的角度来看 hdfs 先从场景出发,我们知道 hdfs 的写文件的流程是这样的:数据以 pipeline 的方式写入 hdfs ,然后对于读取操作,客户端选择其中一个保存块副本的 DataNode 来读数据.考虑这样两个场景:hbase rs 在写 wal log 的时候.如果一个 rs 挂了.那么这个 rs 会转移…

深入理解HDFS 错误恢复

我们从动态的角度来看 hdfs 先从场景出发,我们知道 hdfs 的写文件的流程是这样的:数据以 pipeline 的方式写入 hdfs ,然后对于读取操作,客户端选择其中一个保存块副本的 DataNode 来读数据.考虑这样两个场景:hbase rs 在写 wal log 的时候.如果一个 rs 挂了.那么这个 rs 会转移…

通过MySQL Workbench 将 SQL Server 迁移到GreatSQL

通过MySQL Workbench 将 SQL Server 迁移到GreatSQL 一、概述 MySQL Workbench 提供了可以将Microsoft SQL Server的表结构和数据迁移到 GreatSQL 的功能,此次将通过MySQL Workbench将SQL Server的数据迁移到GreatSQL。 本文章只是简单演示一下单张表的迁移,如果在项目中使用…

我的第一个博客

我的第一个博客我的第一个博客 CBV添加装饰器的三种方式 # CBV添加装饰器 from django.views import View # 需要先导入method_decorator from django.utils.decorators import method_decorator # CBV中django不建议直接给类的方法加装饰器# @method_decorator(login_auth, na…