new 的对象如何不分配在堆而分配在栈上(方法逃逸等)
当能够明确对象不会发生逃逸时,就可以对这个对象做一个优化,不将其分配到堆上,而是直接分配到栈上,这样在方法结束时,这个对象就会随着方法的出栈而销毁,这样就可以减少垃圾回收的压力。
如方法逃逸。
逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。
在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。
当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。
Java在Java SE 6u23以及以后的版本中支持并默认开启了逃逸分析的选项。Java的 HotSpot JIT编译器,能够在方法重载或者动态加载代码的时候对代码进行逃逸分析,同时Java对象在堆上分配和内置线程的特点使得逃逸分析成Java的重要功能。
通过逃逸分析来决定某些实例或者变量是否要在堆中进行分配,如果开启了逃逸分析,即可将这些变量直接在栈上进行分配,而非堆上进行分配。这些变量的指针可以被全局所引用,或者其其它线程所引用。
栈上的空间一般而言是非常小的,只能存放若干变化和小的数据结构,大容量的存储结构是做不到。
所以,逃逸分析的效果只能在特定场景下,满足高频和高数量的容量比较小的变量分配结构,才可以生效。
在Java中每一个对象都有一定的作用域,理论上,一个对象在一块代码中构造,那么也应该在这块代码中被回收,但是实际上,我们经常会让一个对象存活更长的时间,超过定义它的代码块,这就好比一个人逃出了生他养他的地方,我们将这种现象称为逃逸。
逃逸按照行为不同有可以分为方法逃逸和线程逃逸。
方法逃逸是指在某一个方法中构造的对象,在该方法外部可以继续访问这个对象。产生方法逃逸一般是由于返回值返回,或者是将对象的引用设置到传入的参数中,如下图展示了两种产生方法逃逸的例子。
线程逃逸则是在一个线程中构造的对象,能够在另一个线程中使用。这种情况是由于同一个对象被多个线程使用,产生资源占用而导致。下图就是一个通过共享静态变量,来实现线程逃逸的例子,这个例子中的resource对象在多线程的环境下会产生线程逃逸。
至此,我们了解了如何判断一个对象是否会产生逃逸,那么对象逃逸有什么用呢?如果我们能够明确肯定一个对象不会产生逃逸,那么就可以对其进行很多的优化。下面本文就来介绍一下,Java虚拟机在确定对象不发生逃逸的情况下,所进行的一些高效的优化。
栈上分配
众所周知,Java中对象时分配在堆上的,在初始化时,会在堆上分配一块空间,当这个对象不再使用时,会在之后发生垃圾回收时被回收,这是一个Java对象正常的生命周期。但是当能够明确对象不会发生逃逸时,就可以对这个对象做一个优化,不将其分配到堆上,而是直接分配到栈上,这样在方法结束时,这个对象就会随着方法的出栈而销毁,这样就可以减少垃圾回收的压力。
同步消除
在多线程中,对于一个变量操作进行同步操作是效率很低的,当我们确定一个对象不会发生逃逸时,那么就没有必要对这个对象进行同步操作,所以如果代码中有对这种变量操作的同步操作,JVM将会取消同步,从而提升性能。
标量替换
标量指的是没有办法再分解为更小的数据的类型,即Java中的基本类型,我们平时定义的类都属于聚合量。标量替换即是将一个聚合量拆成多个标量来替换,即用一些基本类型来代替一个对象。如果明确对象不会发生逃逸,并且可以进行标量替换的话,那么就可以不创建这个对象,而是直接使用基本类型来代替,这样也就可以节省创建和销毁对象的开销。
虽然基于逃逸技术的优化能够提升程序运行时的性能,但是在实际生产中,对象逃逸的分析默认是不开启的。这是因为分析一个对象是否会发生逃逸消耗比较大,所以,开启逃逸分析并进行这些优化之后得到的效果,并不一定就比不进行优化更好。如果确定开启逃逸分析效率更好,那么可以使用参数-XX:+DoEscapeAnalysis来开启逃逸分析。
TLAB
也就是说,Java中每个线程都会有自己的缓冲区称作TLAB(Thread-local allocation buffer),每个TLAB都只有一个线程可以操作,TLAB结合bump-the-pointer技术可以实现快速的对象分配,而不需要任何的锁进行同步,也就是说,在对象分配的时候不用锁住整个堆,而只需要在自己的缓冲区分配即可。
Java对象分配的过程
- 编译器通过逃逸分析,确定对象是在栈上分配还是在堆上分配。如果是在堆上分配,则进入选项2.
- 如果tlab_top + size <= tlab_end,则在在TLAB上直接分配对象并增加tlab_top 的值,如果现有的TLAB不足以存放当前对象则3.
- 重新申请一个TLAB,并再次尝试存放当前对象。如果放不下,则4.
- 在Eden区加锁(这个区是多线程共享的),如果eden_top + size <= eden_end则将对象存放在Eden区,增加eden_top 的值,如果Eden区不足以存放,则5.
- 执行一次Young GC(minor collection)。
- 经过Young GC之后,如果Eden区任然不足以存放当前对象,则直接分配到老年代。
来源:https://blog.csdn.net/blueheart20/article/details/76167489
来源:https://blog.csdn.net/u011277123/article/details/53908270
来源:https://blog.csdn.net/yangzl2008/article/details/43202969
new 的对象如何不分配在堆而分配在栈上(方法逃逸等)的更多相关文章
- 【LWJGL3】LWJGL3的内存分配设计,第一篇,栈上分配
简介 LWJGL (Lightweight Java Game Library 3),是一个支持OpenGL,OpenAl,Opengl ES,Vulkan等的Java绑定库.<我的世界> ...
- String类型的对象,是保存在堆里还是在栈里呢?
在Java的实现中,new出来的String对象一般是放在堆中的. 如果是 String s ="xxx"; 这种,那就是放在常量池中. JDK6将常量池放在方法区中. 方法区此时 ...
- (转)在.NET程序运行过程中,什么是堆,什么是栈?什么情况下会在堆(栈)上分配数据?它们有性能上的区别吗?“结构”对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的吗?
转自:http://www.cnblogs.com/xiaoyao2011/archive/2011/09/09/2172427.html 在.NET程序运行过程中,什么是堆,什么是栈? 堆也就是托管 ...
- Java中对象并不是都在堆上分配内存的
转(https://blog.51cto.com/13906751/2153924) 前段时间,给星球的球友们专门码了一篇文章<深入分析Java的编译原理>,其中深入的介绍了Java中的j ...
- iOS开发中的内存分配(堆和栈)
进程的内存分区 所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先 ...
- Java内存分配之堆、栈和常量池
Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 3. 堆:存放用new产生的数据 4. 静 ...
- Java内存分配之堆、栈和常量池(转)
摘录自http://www.cnblogs.com/SaraMoring/p/5687466.html Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类 ...
- java内存管理(堆、栈、方法区)
java内存管理 简介 首先我们要了解我们为什么要学习java虚拟机的内存管理,不是java的gc垃圾回收机制都帮我们释放了内存了吗?但是在写程序的过程中却也往往因为不懂内存管理而造成了一些不容易察觉 ...
- JVM内存结构之堆、栈、方法区以及直接内存、堆和栈区别
JVM内存结构之堆.栈.方法区以及直接内存.堆和栈区别 一. 理解JVM中堆与栈以及方法区 堆(heap):FIFO(队列优先,先进先出):二级缓存:*JVM中只有一个堆区被所有线程所共享:对象和数 ...
随机推荐
- Nginx - 根据IP分配不同的访问后端
1. 需求分析 为了在线上环境提供测试分支,规定将某IP转发到测试程序地址.如果是 ngx 直接对外,采用 real_ip 就能够做限制,但是最前端确实一个7层是负载均衡就需要研究一番了. 2. 实践 ...
- Linux 就该这么学 CH04 VIM编辑器和Shell命令脚本
0 概述 1 Vim编辑器 在linux 中一切都是文件,而配置一个服务就是修改其配置文件的参数. vim 编辑器有三种模式:命令模式,末行模式和编辑模式. 命令模式:控制光标移动,对文件进行操作. ...
- 在Django中显示操作数据库的语句
需要在配置中加上这个 LOGGING = { 'version':1, 'disable_existing_loggers':False, 'handlers':{ 'console':{ 'leve ...
- [源码]Python调用C# DLL例子(Python与.Net交互)
K8Cscan C# DLL例子代码 namespace CscanDLL { public class scan { public static string run(string ip) { if ...
- 一个".java"源文件中是否可以包括多个类(不包括内部类)
开通好几个月博客了,但是一直没有时间写,这一段时间准备开始复习了,以后准备好好写博客了,会从基础的内容开始. 一个".java"源文件中可以定义多个类,但最多只能定义一个publi ...
- 在Gridview 中 对日期格式的控制
在数据库中保存日期格式的时候,我们需要在客户端的显示有自己的要求 这就需要对 datatime 类型的数据进行控制,使之显示为你需要的格式 数据库中 如果不对其进行控制,显示的格式为 当在前端页面上进 ...
- mysql 允许在唯一索引的字段中出现多个null值
线上问题:org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [update fl_table ...
- spring data jpa碰到的坑
1.不能从别的类的repository那里 执行另一个类的sql,这样映射会失败. 2.有entity,就要有repository,并且还要有id注解 3.还要多表联查未测试,估计要用map去映射出来 ...
- SQLite接口函数 - C核心api实践与总结
SQLite核心源代码由C语言写就,同时提供了很多的扩展包可应用于其他编程语言和类库,如Python.Ruby.Java.Perl..Net/C#.Qt和ODBC.在很多情况下,针对一种语言有很多扩展 ...
- Docker之网络配置
目的: Docker网络配置 Docker部署SpringCloud项目 Docker网络配置 Docker网络模式介绍 Docker在创建容器时有四种网络模式:bridge/host/conta ...