JVM之对象分配:栈上分配 & TLAB分配

1. Java对象分配流程

2. 栈上分配

2.1 本质:Java虚拟机提供的一项优化技术

2.2 基本思想: 将线程私有的对象打散分配在栈上

2.3 优点:

2.3.1 可以在函数调用结束后自行销毁对象,不需要垃圾回收器的介入,有效避免垃圾回收带来的负面影响

2.3.2 栈上分配速度快,提高系统性能

2.4 局限性: 栈空间小,对于大对象无法实现栈上分配

2.4 技术基础: 逃逸分析

2.4.1 逃逸分析的目的: 判断对象的作用域是否超出函数体[即:判断是否逃逸出函数体]

//user的作用域超出了函数setUser的范围,是逃逸对象
//当函数结束调用时,不会自行销毁user
private User user;
public void setUser(){
    user = new User();
    user.setId(1);
    user.setName("blueStarWei");
}

//u只在函数内部生效,不是逃逸对象
//当函数调用结束,会自行销毁对象u
public void createUser(){
    User u = new User();
    u.setId(2);
    u.setName("JVM");
}

2.5 栈上分配示例

package com.blueStarWei.templet;

public class AllotOnStack {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    private static void alloc() {
        User user = new User();
        user.setId(1);
        user.setName("blueStarWei");
    }
}

2.5.1 上述代码调用了1亿次alloc(),如果是分配到堆上,大概需要1.5GB的堆空间,如果堆空间小于该值,必然会触发GC。

2.5.2 使用如下参数运行,发现不会触发GC

-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

2.5.3 使用如下参数(任意一行)运行,会发现触大量GC

//不使用逃逸分析
-server -Xmx15m -Xms15m -XX:-DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations

//不使用标量替换
-server -Xmx15m -Xms15m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:-EliminateAllocations

2.5.3.1 可以发现:栈上分配依赖于逃逸分析和标量替换

2.5.4 GC日志

[GC (Allocation Failure)  4095K->528K(15872K), 0.0025208 secs]
[GC (Allocation Failure)  4624K->552K(15872K), 0.0012518 secs]
[GC (Allocation Failure)  4648K->608K(15872K), 0.0009262 secs]
......(省略)
3718

2.5.4.1 GC日志解析

参数
作用


备注

   GC
用来区分是 Minor GC 还是 Full GC 的标志(Flag).


这里的 GC 表明本次发生的是 Minor GC.

Allocation Failure
引起垃圾回收的原因.  本次GC是因为年轻代中没有任何合适的区域能够存放需要分配的数据结构而触发的.
4095K->528K
 在本次GC之前和之后的年轻代内存使用情况.  本次GC前,年轻代使用空间4095K, GC后年轻代使用空间为528K
(15872K)
 年轻代的总的大小  
0.0025208 secs
 本次GC使用时间(单位:秒)  

2.5.5 JVM参数解析

参数 作用 备注
-server
使用server模式 只有在server模式下,才可以弃用逃逸分析
-Xmx15m
设置最大堆空间为15m 如果在堆上分配,必然触发大量GC
-Xms15m
设初始对空间为15m  
-XX:+DoEscapeAnalysis
启用逃逸分析 默认启用
-XX:-DoEscapeAnalysis
关闭逃逸分析  
-XX:+PrintGC
打印GC日志  
   -XX:-UseTLAB  关闭TLAB
TLAB(Thread Local Allocation Buffer)

线程本地分配缓存区

-XX:+EliminateAllocations
启用标量替换,允许对象打散分配到栈上
默认启用

-XX:-EliminateAllocations
 关闭标量替换  

3. TLAB 分配

  TLAB,全称Thread Local Allocation Buffer, 即:线程本地分配缓存。这是一块线程专用的内存分配区域。TLAB占用的是eden区的空间。在TLAB启用的情况下(默认开启),JVM会为每一个线程分配一块TLAB区域。

3.1 为什么需要TLAB?

  这是为了加速对象的分配。由于对象一般分配在堆上,而堆是线程共用的,因此可能会有多个线程在堆上申请空间,而每一次的对象分配都必须线程同步,会使分配的效率下降。考虑到对象分配几乎是Java中最常用的操作,因此JVM使用了TLAB这样的线程专有区域来避免多线程冲突,提高对象分配的效率。

3.2 局限性: TLAB空间一般不会太大(占用eden区),所以大对象无法进行TLAB分配,只能直接分配到堆上。

3.3 分配策略:

  一个100KB的TLAB区域,如果已经使用了80KB,当需要分配一个30KB的对象时,TLAB是如何分配的呢?

    此时,虚拟机有两种选择:第一,废弃当前的TLAB(会浪费20KB的空3.4 间);第二,将这个30KB的对象直接分配到堆上,保留当前TLAB(当有小于20KB的对象请求TLAB分配时可以直接使用该TLAB区域)。

  JVM选择的策略是:在虚拟机内部维护一个叫refill_waste的值,当请求对象大于refill_waste时,会选择在堆中分配,反之,则会废弃当前TLAB,新建TLAB来分配新对象。

  【默认情况下,TLAB和refill_waste都是会在运行时不断调整的,使系统的运行状态达到最优。】

3.4 JVM参数解析

参数 作用 备注
-XX:+UseTLAB 启用TLAB 默认启用
-XX:TLABRefillWasteFraction 设置允许空间浪费的比例 默认值:64,即:使用1/64的TLAB空间大小作为refill_waste值
-XX:-ResizeTLAB 禁止系统自动调整TLAB大小  
-XX:TLABSize 指定TLAB大小 单位:B

4. 附件

4.1 User类

packagepackag  com.blueStarWei.templet;

public class User {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
 

5. 参考文献

5.1 《实战Java虚拟机 - JVM故障诊断与性能优化》

5.2  栈上分配、TLAB : https://blog.csdn.net/yangsnow_rain_wind/article/details/80434323

5.3  快速解读GC日志 : https://blog.csdn.net/renfufei/article/details/49230943

原文地址:https://www.cnblogs.com/BlueStarWei/p/9358757.html

时间: 2024-12-08 06:26:54

JVM之对象分配:栈上分配 & TLAB分配的相关文章

【深入浅出-JVM】(7):栈上分配

概念 对那些作用于不会逃逸出方法的对象,在分配内存时,不在将对象分配在堆内存中,而是将对象属性打散后分配在线程私有栈内存上,这样随着方法调用结束,栈上分配打散的对象也被回收掉,不在增加 GC 额外压力. Java 对象分配流程 示例 循环创建1000000000一个对象,阻止栈上分配 栈上分配条件:开启逃逸分析 & 开启标量替换 JVM 参数: 弃用逃逸分析(不允许判断对象是否可以逃逸出函数体) -server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:

十、逃逸分析和栈上分配

Java堆区已经不再是对象实例分配的唯一空间,可以在堆区之外分配内存以提升效率降低频率,逃逸分析即是如此. 什么是逃逸分析? 例如: 一个成员方法的内部实例化了一个对象,如果这个对象被方法外的引用指向了,那么就发生了逃逸现象.JVM在内存分配的时候会分析其是否发生逃逸,如果未发生逃逸的,那么就直接在栈上分配内存空间,其生命周期和线程相同.(也称之为"栈上分配") 原文地址:https://www.cnblogs.com/lay2017/p/8157760.html

如何限制一个类只在堆上分配和栈上分配(StackOnly HeapOnly)

[本文链接] http://www.cnblogs.com/hellogiser/p/stackonly-heaponly.html [题目] 如何限制一个类只在堆上分配和栈上分配? [代码] C++ Code 1234567891011121314   class StackOnly { private:     void *operator new(size_t size) {}; }; class HeapOnly { public:     void destroy(); private

栈和堆上的内存分配和回收

栈上的内存,函数结束的时候这个函数用的内存自动释放:堆上的内存,你必须自己释放,否则就会一直存在,但程序退出时,操作系统会清理进程使用的所有资源,这是就会释放掉堆,堆都没了,内存自然也没了 问题在于,很多时候进程并不会马上退出,比如服务器程序一跑就是几个月,如果分配的那块代码只跑一次那还好,但通常会反复执行,那么分配出去的内存就会越来越多,而又没有机会释放,最终的结果是堆内存耗尽,再次分配失败,程序被迫退出或者耗尽系统资源被操作系统强制杀死.

jvm笔记2--垃圾收集器与内存分配策略

垃圾收集器与内存分配策略 Java运行时,内存的各个部分中,程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈的操作.每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的.因此这几个区域不需要过多考虑回收的问题,因为线程结束时,内存自然就跟着回收了. Java堆和方法区不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,只有在程序运行期间才知道会创建哪些对象,这部分内存的分配

JVM理论:(二/1)内存分配策略

Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决两个问题:给对象分配内存以及回收分配给对象的内存. 对象的分配可能有以下几种方式: 1.JIT编译后被拆散为标量类型并间接地栈上分配 2.对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配 3.少数情况下也会直接分配在老年代 参考下图: 5种内存分配策略 1.对象优先在Eden分配 大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor G

JVM性能调优 第七章 内存分配策略

理解了jvm内存分配策略不仅是程序性能调优的重要知识,还能够给养成自己一种良好的代码思路,一个程序的代码差异往往都是在这里体现出来的. 一.对象优先分配到Eden区域   一般来说,新创建的对象都会直接分配到Eden区域,如果Eden区域内存不够,JVM就会触发GC(垃圾回收),一般来说在JVM中有3种GC: Minor GC:指发生在新生代的垃圾收集动作,非常频繁,速度较快. Major GC:指发生在老年代的GC,出现Major GC,经常会伴随一次Minor GC,同时Minor GC也会

垃圾回收GC:.Net自动内存管理 上(一)内存分配

垃圾回收GC:.Net自动内存管理 上(一)内存分配 前言 .Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态.然而,你或许想要理解GC是怎么工作的.此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述.同时,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理. 引子 为你的应用程序实现合理的资源管理是一件困难的,乏味的工作.这可能会把你的注意力从你当前正在解决的实际问题中转移到它身上.那么,如果有一个现有的机制为开发者管理令人厌恶的内存管理,会

对象类型在内存中的分配

今天,我们来讲讲对象类型在内存中的分配! 对象类型和整型.字符串等类型一样,也是PHP中的一种数据类型,在程序中存储不同类型的数据,在程序运行时它的每一部分内容都要加载到内存中再被使用.那么对象类型的数据在内存中是如何分配的呢?咱们先来了解下内存结构,逻辑上内存大体被分为四段,分别为:栈.堆.数据段和代码段,程序中不同类型数据的声明将会被存放在不同的内存段里面,每段内存的特点是这样的: 1.栈 栈的特点就是空间小但被CPU访问的速度快,适合存放程序中临时创建的变量.由于栈的先进后出的特点,所以栈