jvm对象内存分配
一、jvm简单结构图

1、jvm内存对象分配整体流程:

1、类加载子系统和方法区
类加载子系统负责从文件系统或者网络中加载Class信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,
方法区中可能还会存放运行时常量池信息,包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)。
2、java堆
堆空间在jvm启动的时候即根据设置(后续 jvm参数调优)创建,是java程序中最主要的内存工作区域。堆空间是所有线程共享的。
jvm分配对象,一般来说,new个新对象,都是分配在堆空间的。其实这也不是绝对的,存在部分对象的分配是在栈上以及TLAB中。
TLAB:全称 Thread Local Allocation Buffer,线程本地分配缓存区,是一个为线程分配的专用的内存使用区域。在使用参数 -XX:UseTLAB 打开设置的时候,在进行
线程初始化的时候,会去分配一小块的内存区域供线程使用,这样如果使用,直接在自己的内存区域就可以直接分配使用了,提高效率。
默认的空间大小为Eden区的 1%,-XX:TLABWasteTargetPercent可以设置 TLAB 占 Eden区的百分比大小。
对象分配流程图:

上图:如果开启栈上分配,JVM会先进行栈上分配,如果没有开启栈上分配或则不符合条件的则会进行TLAB分配,如果TLAB分配不成功,再尝试在eden区分配,如果对象满足了直接进入老年代的条件①,那就直接分配在老年代。
①:大对象可直接进入老年代
大对象是指需要大量的连续的内存空间的java对象,典型的例子就是N长的字符串和数组。大对象在分配的时候,很纠结,很可能会出现提前触发GC来获取到足够的连续的空间来分配他们。JVM提供了一个值参数: -XX:PreteenureSizeThreshold参数,让对象大于这个参数时直接进入老年代分配。这样做,可以避免年轻代Eden和两个Survivor区域发生大量的内存复制,提高效率。
2.1、栈上分配
在JVM中,堆是线程共享的,因此堆上的对象对于各个线程都是共享和可见的,只要持有对象的引用,就可以访问堆中存储的对象数据。虚拟机的垃圾收集系统可以回收堆中不再使用的对象,
但对于垃圾收集器来说,无论筛选可回收对象,还是回收和整理内存都需要耗费时间。
如果确定一个对象的作用域不会逃逸出方法之外,那可以将这个对象分配在栈上,这样,对象所占用的内存空间就可以随栈帧出栈而销毁。在一般应用中,不会逃逸的局部对象所占的比例很大,
如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,无须通过垃圾收集器回收,可以减小垃圾收集器的负载。
JVM允许将线程私有的对象打散分配在栈上,而不是分配在堆上。分配在栈上的好处是可以在函数调用结束后自行销毁,而不需要垃圾回收器的介入,从而提高系统性能。
栈上分配的技术基础:
一是逃逸分析:逃逸分析的目的是判断对象的作用域是否有可能逃逸出函数体。关于逃逸分析的问题可以看我另一篇文章:
二是标量替换:允许将对象打散分配在栈上,比如若一个对象拥有两个字段,会将这两个字段视作局部变量进行分配。
只能在server模式下才能启用逃逸分析,参数-XX:DoEscapeAnalysis启用逃逸分析,参数-XX:+EliminateAllocations开启标量替换(默认打开)。选项-XX:+PrintEscapeAnalysis查看逃逸分析的筛选结果。
注意:在部分JDK1.6版本和后续的JDK版本(64位系统)中,-client参数已经不起作用了,Server模式成为唯一
2.2、TLAB分配
TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。
由于对象一般会分配在堆上,而堆是全局共享的。因此在同一时间,可能会有多个线程在堆上申请空间。因此,每次对象分配都必须要进行同步(虚拟机采用CAS配上失败重试的方式保证更新操作的原子性),
而在竞争激烈的场合分配的效率又会进一步下降。JVM使用TLAB来避免多线程冲突,在给对象分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高了对象分配的效率。
TLAB本身占用eEden区空间,在开启TLAB的情况下,虚拟机会为每个Java线程分配一块TLAB空间。参数-XX:+UseTLAB开启TLAB,默认是开启的。TLAB空间的内存非常小,
缺省情况下仅占有整个Eden空间的1%,当然可以通过选项-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小。
由于TLAB空间一般不会很大,因此大对象无法在TLAB上进行分配,总是会直接分配在堆上。TLAB空间由于比较小,因此很容易装满。比如,一个100K的空间,已经使用了80KB,
当需要再分配一个30KB的对象时,肯定就无能为力了。这时虚拟机会有两种选择,第一,废弃当前TLAB,这样就会浪费20KB空间;第二,将这30KB的对象直接分配在堆上,保留当前的TLAB,
这样可以希望将来有小于20KB的对象分配请求可以直接使用这块空间。实际上虚拟机内部会维护一个叫作refill_waste的值,当请求对象大于refill_waste时,会选择在堆中分配,
若小于该值,则会废弃当前TLAB,新建TLAB来分配对象。这个阈值可以使用TLABRefillWasteFraction来调整,它表示TLAB中允许产生这种浪费的比例。默认值为64,
即表示使用约为1/64的TLAB空间作为refill_waste。默认情况下,TLAB和refill_waste都会在运行时不断调整的,使系统的运行状态达到最优。
如果想要禁用自动调整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB,并使用-XX:TLABSize手工指定一个TLAB的大小。
-XX:+PrintTLAB可以跟踪TLAB的使用情况。一般不建议手工修改TLAB相关参数,推荐使用虚拟机默认行为。
3、java栈
每一个java虚拟机线程都有一个私有的java栈,一个线程的java栈在线程创建的时候被创建,java栈中保存着帧信息,
java栈中保存着局部变量、方法参数,同时和java方法的调用、返回密切相关。
4、垃圾回收
垃圾回收系统是java虚拟机的重要组成部分,垃圾回收器可以对方法区、java堆和直接内存进行回收。其中,java堆是垃圾收集器的工作重点。和C/C++不同,java中所有的对象空间释放都是隐式的,也就是说,java中没有类似free()或者delete()这样的函数释放指定的内存区域。对于不再使用的垃圾对象,垃圾回收系统会在后台默默工作,默默查找、标识并释放垃圾对象,完成包括java堆、方法区和直接内存中的全自动化管理。
5、执行引擎
执行引擎是java虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提高执行效率,会使用即时编译(just in time)技术将方法编译成机器码后再执行。
Java HotSpot Client VM(-client),为在客户端环境中减少启动时间而优化的执行引擎;本地应用开发使用。(如:eclipse)
Java HotSpot Server VM(-server),为在服务器环境中最大化程序执行速度而设计的执行引擎。应用在服务端程序。(如:tomcat)
Java HotSpot Client模式和Server模式的区别当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译器,
而-server模式启动的虚拟机采用相对重量级,代号为C2的编译器.
C2比C1编译器编译的相对彻底,服务起来之后,性能更高JDK安装目录/jre/lib/(x86、i386、amd32、amd64)/jvm.cfg文件中的内容,
-server和-client哪一个配置在上,执行引擎就是哪一个。如果是JDK1.5版本且是64位系统应用时,-client无效。
--64位系统内容
-server KNOWN
-client IGNORE
--32位系统内容
-server KNOWN
-client KNOWN
注意:在部分JDK1.6版本和后续的JDK版本(64位系统)中,-client参数已经不起作用了,Server模式成为唯一
二、对象分代结构
JVM根据对象存活周期的不同,将堆分成几个不同的结构区域,一般来说是分为:新生代(young)、老年代(old)、永久代(permanent)。
2.1、为什么要进行分代
堆内存是虚拟机管理的最大的一块内存区域,也是垃圾回收最为频繁的区域。程序运行时的所有对象实例都在这里保存。内存分代管理就是为对象的内存分配和销毁回收提高效率。试想一下,如果不进行分代划分,所有的对象都放在一起,那些新生的和一些 生命周期很长的都在一起,每次进行GC的时候,都必须得扫描遍历全部的对象,这个过程是十分耗费资源的,会严重的影响GC的效率。
有了内存的分代,会大大提升垃圾回收的效率。新生成的对象在新生代中分配内存,经过几次的垃圾回收依然存活下来的就存放到老年代中,永久代中存放静态属性以及类信息等。新生代中的对象,生命周期最短,相对的来说GC的频率就很高。老年代的生命
周期较长,不需要进行频繁的内存回收。永久代基本不需要进行回收操作。当然了,回收机制是可以根据不同代的特点来选择合适的垃圾回收算法的。
2.2、内存分代
JVM将内存分为新生代(Eden、Survivor:From和To)、老年代以及永久代。永久代中主要存放静态变量、常量以及类信息等,基本不进行垃圾回收。新生代和老年代是垃圾回收的主要区域。
内存的分代示意图如下:

2.2.1、新生代
新生代是程序运行过程中,大部分新生成的对象分配的区域。新生代对象,生命周期很短,垃圾回收相当的频繁,一般,一次的垃圾回收,能回收掉70-90的空间,回收效率很高。
新生代分为三块:一块较大的Eden区和两块较小的Survivor区(两块大小相同),默认的大小比率为8:1:1,划分的目的是因为Hotspot采用复制算法来回收新生代,设置这个比率充分利用内存空间,减少浪费。
Eden区分配新生成的对象(大对象出外,上面有提及),当Eden区没有足够的空间去分配新对象的时候,出发一次minor GC。
程序运行中,To区域始终需要保持空状态,对象保存在Eden和From区域。新生成对象首先是存放在Eden区域的,当触发Minor GC的时候,将Eden中幸存对象拷贝到From区域中并且年龄加1,然后检查To区域已有
对象是否可达可用,如若不需要了,对其进行一次清理,幸存的对象年龄加1。将From区域的对象拷贝过来,最后交换From与To的名称。保持To区域的空。
2.2.2、老年代
新生代young中,经过了多次的GC后还能幸存下来的对象(具体的多少次,可进行设置阈值控制,默认15),进入老年代中。老年代中的对象,生命周期长,GC的频率低,并且回收的效率上也相对较低。
2.2.3、永久代
永久代中存储的是类信息、常量、静态变量等数据。一般不进行GC。
jvm对象内存分配的更多相关文章
- JVM初探- 内存分配、GC原理与垃圾收集器
JVM初探- 内存分配.GC原理与垃圾收集器 标签 : JVM JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. 除了在概念 ...
- JVM总结(二):JVM的内存分配策略
这节我们总结一下JVM中的内存分配策略.目录如下: 内存分配策略 对象优先在新生代Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保 内存分配策略 Java ...
- JVM的内存分配垃圾回收策略
之前看过<深入了解Java虚拟机>感觉容易忘,今天写一篇博客加深一下印象. JVM的内存分配和垃圾回收(GC)主要发生在Java堆中.而Java堆根据对象的存活时间可以分为新生代和老年代, ...
- 一夜搞懂 | JVM GC&内存分配
前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习导图 一.为什么要学习GC&内存分配? 时代发展到现在,如今的内存动态分配与内存回收技术已经相当成 ...
- [Java]Java类和对象内存分配详解
描述 代码说明: 一.当Person p1 = new Person();第一次被调用时需要做两件事: 1.先判断类加载器是否加载过Person类,如果没有则加载到Person类型到方法区 2.在堆中 ...
- jvm的内存分配总结
最近看了周志明版本的<深入理解Java虚拟机>第一版和第二版,写的很好,收获很多,此处总结一下. jvm中内存划分: 如上图,一共分为五块,其中: 线程共享区域为: 1.java堆 ...
- JVM的内存分配与垃圾回收策略
自动内存管理机制主要解决了两个问题:给对象分配内存以及回收分配给对象的内存. >>垃圾回收的区域 前面的笔记中整理过虚拟机运行数据区,再看一下这个区域: 注意在这个Runtime Data ...
- Java 对象内存分配与回收
JVM内存区域模型: * 程序计数器,内存区域极小,是当前线程的字节码执行行号指示器: * 虚拟机栈.本地方法栈,即平时所说的“栈”,是虚拟机用来执行方法(包括Java.非Java方法)时,使用的临时 ...
- 浅谈JVM与内存分配
一.程序内存分配 初始内存分配 当一个程序准备运行时,它首先向java虚拟机要内存,但是java虚拟机本身没有权限,它只能向操作系统申请内存,此时java虚拟机会拥有一个初始内存, 此处额外说明一下e ...
随机推荐
- zabbix利用自带模板-监控mysql性能
环境: zabbix3.4.4 mariadb 5.5.56 要求: 利用zabbix 自带的模板 监控mariadb 上的 并发连接数,慢查询数量,增删改查.请求流量带宽,mysql响应流量带宽等 ...
- c++采集windows操作系统名称
WINAPI windows通过c++获取操作系统主要分两种: 1. windows是8.1版本以下版本:获取操作系统可以通过windows提供的api中GetVersionEx函数来获取 2. wi ...
- Spring学习之旅(六)--SpringMVC集成
对大多数 Java 开发来说,基于 web 的应用程序是我们主要的关注点. Spring 也提供了对于 web 的支持,基于 MVC 模式的 Spring MVC 能够帮助我们灵活和松耦合的完成 we ...
- C#开发BIMFACE系列5 服务端API之文件直传
BIMFACE使用了分布式对象存储来存储用户上传的模型/图纸文件.如使用普通的文件上传接口, 文件流会通过BIMFACE的服务器,再流向最终的分布式存储系统,整个上传过程会受BIMFACE服务器的带宽 ...
- GitExtensions使用教程
GitExtensions工具使用教程 第一步:安装 1.双击:GitExtensions24703SetupComplete.msi <ignore_js_op> image001.pn ...
- 新手学习FFmpeg - 调用API完成录屏并进行H.264编码
Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...
- 学生管理系统 Python语言
def show_student(): print(('*'*20).center(55)) print('1.添加学生信息'.center(50)) print('2.修改学生信息'.center( ...
- Mybatis案例升级版——小案例大道理
纯Mybatis案例升级版——小案例大道理 前言: 这几天看了一本书<原则>,在上面看到了一句话叫“每个人都把自己眼界的局限当成世界的局限”,大学生是
- 为什么有了Compose和Swarm,还会有Kubernetes的出现?
一.k8s设计思想更先进 k8s的主要设置思想,是从更宏观的角度,以统一的方式来定义任务之间的各种关系 1.k8s的核心功能图 2.k8s的全局架构图 kube-apiserver:API服务 Kub ...
- E-MAZE_2019牛客暑期多校训练营(第二场)
题意 给出n行m列的迷宫0可走1不可走,有两个操作,操作1变换点(a,b)的值,操作2查询(1,a)到(n,b)的方案数 题解 设\(F[i][j]\)为第i-1行到达第i行第j列的方案数,若点\(( ...