专注于Java领域优质技术,欢迎关注

作者 l Hollis 来源 l Hollis(ID:hollischuang)

JVM内存结构,是很重要的知识,相信每一个静心准备过面试的程序员都可以清楚的把堆、栈、方法区等介绍的比较清楚。

上图,是一张在作者根据《Java虚拟机规范(Java SE 8)》中描述的JVM运行时内存区域结构画的。

很多人都知道Java对象是在堆内存中分配空间的(JIT优化除外),也知道内存分配过程中是线程安全的,那么虚拟机到底是如何保证线程安全的呢?本文就来简单介绍一下。

1 Java对象的内存分配

我们知道,Java是一门面向对象的语言,我们在Java中使用的对象都需要被创建出来,在Java中,创建一个对象的方法有很多种,如使用new、使用反射、使用Clone方法等,但是无论如何,对象在创建过程中,都需要进行内存分配。

拿最常见的new关键字举例,当我们使用new创建对象后代码开始运行后,虚拟机执行到这条new指令的时候,会先检查要new的对象对应的类是否已被加载,如果没有被加载则先进行类加载。

在类加载检查通过之后,就需要给对象进行内存分配了,分配的内存主要用来存放对象的实例变量。

在进行内存分配时,需要根据对象中的实例变量情况等信息确定需要分配的空间大小,然后从Java堆中划分出这样一块区域(假设没有JIT优化)。

根据JVM使用的垃圾回收器的类型,因其回收算法不同,会导致堆中内存分配情况不同。如标记-清楚算法回收后的内存中会有大量不连续的内存碎片,在给新的对象分配的时候,就需要通过"空闲列表"来确定一块空闲区域。(这部分不是本文重点,读者可以自行学习一下。)

无论那种方式,最终都需要确定出一块内存区域,用于给新建对象分配内存。我们知道,对象的内存分配过程中,主要是对象的引用指向这个内存区域,然后进行初始化操作。

那么问题就来了:

在并发场景中,如何内存分配过程的线程安全性?如果两个线程先后把对象引用指向了同一个内存区域,怎么办。

2 TLAB

一般有两种解决方案:

  • 1、对分配内存空间的动作做同步处理,采用CAS机制,配合失败重试的方式保证更新操作的线程安全性。
  • 2、每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块"私有"内存中分配,当这部分区域用完之后,再分配新的"私有"内存。

方案1在每次分配时都需要进行同步控制,这种是比较低效的。

方案2是HotSpot虚拟机中采用的,这种方案被称之为TLAB分配,即Thread Local Allocation Buffer。这部分Buffer是从堆中划分出来的,但是是本地线程独享的。

这里值得注意的是,我们说TLAB时线程独享的,但是只是在“分配”这个动作上是线程独占的,至于在读取、垃圾回收等动作上都是线程共享的。而且在使用上也没有什么区别。

另外,TLAB仅作用于新生代的Eden Space,对象被创建的时候首先放到这个区域,但是新生代分配不了内存的大对象会直接进入老年代。因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

所以,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被垃圾回收或者被移到Survivor Space、Old Gen等。

不知道大家有没有想过,我们使用了TLAB之后,在TLAB上给对象分配内存时线程独享的了,这就没有冲突了,但是,TLAB这块内存自身从堆中划分出来的过程也可能存在内存安全问题啊。

所以,在对于TLAB的分配过程,还是需要进行同步控制的。但是这种开销相比于每次为单个对象划分内存时候对进行同步控制的要低的多。

虚拟机是否使用TLAB是可以选择的,可以通过设置-XX:+/-UseTLAB参数来指定。

3 总结

为了保证Java对象的内存分配的安全性,同时提升效率,每个线程在Java堆中可以预先分配一小块内存,这部分内存称之为TLAB(Thread Local Allocation Buffer),这块内存的分配时线程独占的,读取、使用、回收是线程共享的。

可以通过设置-XX:+/-UseTLAB参数来指定是否开启TLAB分配。

参考资料

《深入理解Java虚拟机》

https://www.cnblogs.com/straybirds/p/8529924.html https://www.zhihu.com/question/56538259

JAVA | Java对象的内存分配过程是如何保证线程安全的?的更多相关文章

  1. 灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?(阿里面试)

    JVM内存结构,是很重要的知识,相信每一个静心准备过面试的程序员都可以清楚的把堆.栈.方法区等介绍的比较清楚. 上图,是一张在作者根据<Java虚拟机规范(Java SE 8)>中描述的J ...

  2. Java基础-对象的内存分配与初始化(一定要明白的干货)

    首先,什么是类的加载?类的加载由类加载器执行.该步骤将查找字节码(classpath指定目录),并从这些字节码中创建一个Class对象.Java虚拟机为每种类型管理一个独一无二的Class对象.也就是 ...

  3. Java中对象的内存分配机制

    一.内存划分 Java把内存划分为两种,一种是栈内存,另一种是堆内存. 1.栈内存 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配.当在一段代码块定义一个变量时,Java就在栈 ...

  4. 源码分析:Java对象的内存分配

    Java对象的分配,根据其过程,将其分为快速分配和慢速分配两种形式,其中快速分配使用无锁的指针碰撞技术在新生代的Eden区上进行分配,而慢速分配根据堆的实现方式.GC的实现方式.代的实现方式不同而具有 ...

  5. Java中的成员初始化顺序和内存分配过程

    Java中的成员初始化顺序和内存分配过程 原帖是这样描述的: http://java.dzone.com/articles/java-object-initialization?utm_source= ...

  6. Java虚拟机7:内存分配原则

    前言 对象的内存分配,往大的方向上讲,就是在堆上分配,少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节决定于当前使用的是哪种垃圾收集器组合,当然还有虚拟机中与内存相关的参数 ...

  7. java程序运行时内存分配详解

    java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下   一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...

  8. Java虚拟机11:内存分配原则

    前言 JVM的自动内存管理要自动化的解决两个问题:对象分配内存以及回收分配给对象的内存.对象的内存分配一般是指在堆上分配,少数情况下也可能会直接分配在老年代上,对象主要分配在新生代的Eden 区上,如 ...

  9. 《深入理解 java 虚拟机》学习 -- 内存分配

    <深入理解 java 虚拟机>学习 -- 内存分配 1. Minor GC 和 Full GC 区别 概念: 新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java ...

随机推荐

  1. 通过nginx部署前端代码实现前后端分离

    实现前后端分离,可以让前后端独立开发.独立部署.独立单测,双方通过JSON进行数据交互. 对于前端开发人员来说,不用每次调试都需要启动或配置Java/Tomcat运行环境:对于后端开发人员来说 ,也不 ...

  2. android黑白屏的问题

    你会很奇怪,为什么有些app启动时,会出现一会儿的黑屏或者白屏才进入Activity的界面显示,但是有些app却不会如QQ手机端,的确这里要做处理一下.这里先了解一下为什么会出现这样的现象,其实很简单 ...

  3. Hibernate 5.x 配置 C3P0 数据库连接池

    Hibernate 5.x 配置 C3P0 连接池 首先准备好需要的 jar 包,这里我准备的是 Hibernate 5.2.10 版本: hibernate-core-5.2.10.Final.ja ...

  4. pandas 之 group by 过程

    import numpy as np import pandas as pd Categorizing a dataset and applying a function to each group ...

  5. django memcached/redis缓存 =====缓存session

    全站使用 例如 博客等缓存,通过中间件实现全站缓存. 加缓存中间件,那么多中间件加在什么位置? 请求时:缓存加在中间件里的最后一个,比如一次经过1.2.3.4中间件,加在4 返回事:缓存加在中间件里的 ...

  6. CentOS6.7安装部署php5(详解安装选项与主配置文件)

    模块安装---PHP 编译环境:gcc  gcc-c++   pcre-devel  openssl-devel   libxml2   libxml2-devel   bzip   bzip-dev ...

  7. 【Yellow Cards CodeForces - 1215A 】【贪心】

    该题难点在于求最小的离开数,最大的没什么好说的,关键是求最小的. 可以这样去想,最小的离开数就是每个人获得的牌数等于他所能接受的最大牌数-1,这样就可以直接比较m=a1(k1-1)+a2(k2-1)与 ...

  8. mysql的binlog空间维护

    .Binlog空间维护 一,显示当前的logs文件记录 show master logs; 二,清空n天前的日志,减少磁盘空间 PURGE MASTER LOGS BEFORE DATE_SUB(CU ...

  9. 【比赛游记】THUSC2019酱油记

    往期回顾:THUWC2019酱油记 时间过得真快呐-- 上次在 THUSC 手玩 AI 的情景还未走远,明天却要迎来全新一年的赛事了-- 掐指一算,作为一个真正的 OIer 的时光也不多了啊 day ...

  10. discuz x3.3排行首页图片显示更多的图片

    找到\source\include\misc\misc_ranklist_index.php文件,修改41行 $ranklist = getranklist_pictures_index(9); 为 ...