一、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对象内存分配的更多相关文章

  1. JVM初探- 内存分配、GC原理与垃圾收集器

    JVM初探- 内存分配.GC原理与垃圾收集器 标签 : JVM JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. 除了在概念 ...

  2. JVM总结(二):JVM的内存分配策略

    这节我们总结一下JVM中的内存分配策略.目录如下: 内存分配策略 对象优先在新生代Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保 内存分配策略 Java ...

  3. JVM的内存分配垃圾回收策略

    之前看过<深入了解Java虚拟机>感觉容易忘,今天写一篇博客加深一下印象. JVM的内存分配和垃圾回收(GC)主要发生在Java堆中.而Java堆根据对象的存活时间可以分为新生代和老年代, ...

  4. 一夜搞懂 | JVM GC&内存分配

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习导图 一.为什么要学习GC&内存分配? 时代发展到现在,如今的内存动态分配与内存回收技术已经相当成 ...

  5. [Java]Java类和对象内存分配详解

    描述 代码说明: 一.当Person p1 = new Person();第一次被调用时需要做两件事: 1.先判断类加载器是否加载过Person类,如果没有则加载到Person类型到方法区 2.在堆中 ...

  6. jvm的内存分配总结

    最近看了周志明版本的<深入理解Java虚拟机>第一版和第二版,写的很好,收获很多,此处总结一下.   jvm中内存划分:   如上图,一共分为五块,其中: 线程共享区域为: 1.java堆 ...

  7. JVM的内存分配与垃圾回收策略

    自动内存管理机制主要解决了两个问题:给对象分配内存以及回收分配给对象的内存. >>垃圾回收的区域 前面的笔记中整理过虚拟机运行数据区,再看一下这个区域: 注意在这个Runtime Data ...

  8. Java 对象内存分配与回收

    JVM内存区域模型: * 程序计数器,内存区域极小,是当前线程的字节码执行行号指示器: * 虚拟机栈.本地方法栈,即平时所说的“栈”,是虚拟机用来执行方法(包括Java.非Java方法)时,使用的临时 ...

  9. 浅谈JVM与内存分配

    一.程序内存分配 初始内存分配 当一个程序准备运行时,它首先向java虚拟机要内存,但是java虚拟机本身没有权限,它只能向操作系统申请内存,此时java虚拟机会拥有一个初始内存, 此处额外说明一下e ...

随机推荐

  1. tf.name_scope() 和 tf.variable_scope() 的用法和玄机

    https://my.oschina.net/liusicong/blog/1593467

  2. java学习之- 线程运行状态

    标签(空格分隔): 线程运行状态 线程的运行状态: 如下是是我编写的一个图,大家可以作为参考: 1.new一个thread子类也是创建了一个线程: 2.创建完毕之后start()-----运行, 3. ...

  3. SSH密码和秘钥认证原理

    SSH登录方式主要分为两种: 1. 用户名密码验证方式 说明: (1) 当客户端发起ssh请求,服务器会把自己的公钥发送给用户: (2) 用户会根据服务器发来的公钥对密码进行加密: (3) 加密后的信 ...

  4. ranger-hdfs 插件组权限测试

    当hdfs文件对外是公开的则该其他用户就算没有配置相关的权限一样可以进行相关的操作.当hdfs文件对外权限是没有开放的,其他用户若需要进行相关操作则需要通过Ranger进行相关权限的配置. 首先  / ...

  5. Egret白鹭开发微信小游戏程序跳转功能(由一个小游戏跳转到另一个小游戏)

    假设我们要实现的功能是从小游戏A跳转到小游戏B 对于小游戏A: (1)在platform.ts中添加代码如下: /** * 平台数据接口. * 由于每款游戏通常需要发布到多个平台上,所以提取出一个统一 ...

  6. Leetcode之深度优先搜索(DFS)专题-1123. 最深叶节点的最近公共祖先(Lowest Common Ancestor of Deepest Leaves)

    Leetcode之深度优先搜索(DFS)专题-1123. 最深叶节点的最近公共祖先(Lowest Common Ancestor of Deepest Leaves) 深度优先搜索的解题详细介绍,点击 ...

  7. bdtrans 一个命令行下的机器翻译工具

    现如今,机器翻译技术已经越来越成熟了,尽管从整体来看机器翻译的结果还不是特别如意,但是也足以应付一般的翻译需求了.近几年机器翻译平台层出不穷,国外比较出名的翻译平台有Google翻译.必应翻译等,国内 ...

  8. StackOverflow 周报 - 与高关注的问题过过招(Java)

    本篇文章是 Stack Overflow 周报的第二周,共收集了 4 道高关注的问题和对应的高赞回答.公众号「渡码」为日更,欢迎关注. DAY1.  serialVersionUID 的重要性 关注: ...

  9. 【管理学】PDCA

  10. manacher --- 暂 旧版本

    #include<bits/stdc++.h> using namespace std; ; ]; int n; ]; void manacher() { memset(len, , si ...