精华内容
下载资源
问答
  • 深入理解Java的堆内存线程内存

    千次阅读 2018-01-05 21:47:48
    我们都知道Java对象都是在中创建的(开启逃逸分析的情况除外),我们也知道比如一个线程中有一段这样的代码:public class A{ public int xxx;} A a = new A();会在中创建一个对象,并引用a 指向了中对象的...

    我们都知道Java对象都是在堆中创建的(开启逃逸分析的情况除外),我们也知道比如一个线程中有一段这样的代码:public class A{ public int xxx;}

    A a = new A();会在堆中创建一个对象,并引用a 指向了堆中对象的内存地址,也就是主内存中。

    也就是说线程中的引用指向了主内存中的对象地址,很多Java程序员甚至以为因为持有引用,所以对这个引用的赋值或者读取都是直接根据地址操作主内存的对象,其实并不是这样的。

    如果按照这个逻辑,线程中操作的对象就是主内存中对象(为了好理解,我直接认为主内存就是堆);直接操作堆中对象, 那就是只有一个堆中对象,也不会存在多线程下不一致问题了,因为大家都是通过地址操作同一个对象,只有一个版本,就不会有不一致问题。

    可事实并不是这样。线程内存和主内存是不一样的。当线程要读取a.xxx的时候,其实是通过该引用持有的内存地址去堆中读取这个对象的属性值,赋值给线程中的变量a.xxx;修改也一样,修改完了后将这个值覆盖堆中a的xxx属性的值(怎么实现我稍后讲);

    Java操作内存相关的指令有8个,lock(锁定),unlock(解锁),read(读取),load(载入),use(使用),assign(赋值),store(存储)

    线程对象的操作主要是通过这个几个指令实现的,而不是我们想象的直接操作。

     

    所以多线程下的时候,每个线程都去堆中读取对象的值,拷贝到自己线程变量中使用,修改完了再覆盖回去。这才会出现不一致和读到被别人修改的数据。

     

    volatile关键字的作用之一就是每次修改后都会马上回写到主存中,操作系统的缓存一致性原则会使其线程内存中该值失效,或者说高速缓存中的其他持有该引用的内存失效,当其他线程使用这个关键字的时候,会先检查该引用是否失效,失效会立马回主存重新读取,保证了线程的可见性。如果按照线程对象就是直接操作堆中对象,那就根本就不需要这个关键字了,简单想象就知道线程中的对象也不是堆中的对象这个事实了,使用这个关键字就是希望线程中的对象和堆中的对象是一致的,不再赘述。

    对于大部分对象有效,但是对于基本类型的包装类无效,并不可见,本人亲测发现的,希望看到的人注意。

    回答刚才留下的坑,那我们到底是怎么去堆中读取对象的内容的呢,比如上面的a.xxx,   对象的属性的开始地址相对对象开始地址是有一个偏移量的,

    每个类型都有其规定的长度,只要从开始地址读取这个类型长度的地址就可以了。下面演示一种牛B的修改内存地址处值的方法。

     

    这个偏移量的获取方法如下: 
            Field  field = a.getClass().getDeclaredField("xxx");
    long sexOffset = unsafe.objectFieldOffset(field);

                                    sexOffset 就是这个偏移量;

     

                                  unsafe 是sun.misc.Unsafe类,是不能通过new出来的。可以通过反射获取,

                    好像在我另一篇博文中就有介绍了,Unsafe类之无锁机制与线程安全中已经贴除出了所有代码。

         public native Object getObjectVolatile(Object arg0, long arg1);这个方法的实现类中就可以通过对象引用和偏移地址来获取其属性值。

                这个类中还有很多牛B方法,很多都是通过传说的CAS算法实现的的,其实这么算法没那么复杂就是比较和替换,比较堆中的该地址处的值是否和期望值一样,一样就执            行相应的修改操作,并返回真,不一样就放弃操作,返回假。

               现在我们讲完了线程对象和主内存对象的内存操作,拜拜。下期我有啥想法再写出来,欢迎纠正说的不对的地方。

     

     

     

    展开全文
  • 如果一个线程发生堆内存溢出,或者栈内存溢出,其他线程是否还会继续工作 不废话,先上答案,不管是堆内存溢出,或者栈内存溢出,其余线程都会继续工作 1:首先测试堆内存溢出 **1.1:试用IDEA测试,代码如下:** ...

    如果一个线程发生堆内存溢出,或者栈内存溢出,其他线程是否还会继续工作

    不废话,先上答案,不管是堆内存溢出,或者栈内存溢出,其余线程都会继续工作

    1:首先测试堆内存溢出

    	**1.1:试用IDEA测试,代码如下:**
    

    在这里插入图片描述

    	**1.2:然后设置运行内存大小**
    

    在这里插入图片描述

    	1.3:指定main方法,并打开jvisualvm
    	
    	jvisualvm的位置在JDK的bin文件夹内
    

    在这里插入图片描述

    查看代码的运行的jvisualvm监控页面
    

    在这里插入图片描述

    可以看到在10点25分33秒发生过一次GC,此时我们去翻看我们的输出记录,是否存在内存溢出

    在这里插入图片描述

    而且在发生堆内存溢出之后,其他线程仍然在继续执行。

    2:栈内存溢出跟堆内存溢出一样,其他线程依旧会执行

    2.1:栈内存代码如下
    

    在这里插入图片描述

    2.2:结果如下:
    

    在这里插入图片描述

    线程内存溢出后,线程占用的内存会快速释放,不会影响其他线程的继续运行。
    
    展开全文
  • 首先,在JVM的内存结构中,比较常见的两个区域是堆内存和栈内存关于这两个概念的介绍一般如下: 1、堆是线程共享的内存区域,栈是线程独享的内存区域。 2、堆中主要存放对象实例,栈中主要存放各种基本数据类型、...
  • java线上故障分析-线程dump,堆内存分析
  • 通过之前的验证,已经知道,如果一个线程内部发生了内存溢出,会导致这个线程中断且消亡。 比如说: 设置最大为20M 然后开启两个线程,一个线程里面不断的读数据 直到内存溢出。 ...

    通过之前的验证,已经知道,如果一个线程内部发生了内存溢出,会导致这个线程中断且消亡。
    比如说:
    在这里插入图片描述

    设置最大堆为20M

    然后开启两个线程,一个线程里面不断的读数据 直到内存溢出。

    展开全文
  • 主要介绍了JVM 堆内存溢出后,其他线程是否可继续工作?,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 转载声明:Java堆内存线程共享的!面试官:你确定吗? Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解。可以说,关于...

    转载声明:Java堆内存是线程共享的!面试官:你确定吗?

    Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解。可以说,关于JVM的相关知识,基本是每个Java开发者必学的知识点,也是面试的时候必考的知识点。

    在JVM的内存结构中,比较常见的两个区域就是堆内存和栈内存(如无特指,本文提到的栈均指的是虚拟机栈),关于堆和栈的区别,很多开发者也是如数家珍,有很多书籍,或者网上的文章大概都是这样介绍的:

    1. 堆是线程共享的内存区域,栈是线程独享的内存区域。
    2. 堆中主要存放对象实例,栈中主要存放各种基本数据类型、对象的引用。

    但是,作者可以很负责任的告诉大家,以上两个结论均不是完全正确的。在开始进入正题之前,请允许我问一个和这个问题看似没有任何关系的问题:Java对象的内存分配过程是如何保证线程安全的?

    Java对象的内存分配过程是如何保证线程安全

    我们知道,Java是一门面向对象的语言,我们在Java中使用的对象都需要被创建出来,在Java中,创建一个对象的方法有很多种(可以思考下一共有几种),但是无论如何,对象在创建过程中,都需要进行内存分配。对象的内存分配过程中,主要是对象的引用指向这个内存区域,然后进行初始化操作

    但是,因为堆是全局共享的,因此在同一时间,可能有多个线程在堆上申请空间,那么在并发场景中,如果两个线程先后把对象引用指向了同一个内存区域,怎么办

    为了解决这个并发问题,对象的内存分配过程就必须进行同步控制。但是我们都知道,无论是使用哪种同步方案(实际上虚拟机使用的可能是CAS),都会影响内存的分配效率。

    而Java对象的分配是Java中的高频操作,所有,人们想到另外一个办法来提升效率。这里我们重点说一个HotSpot虚拟机的方案:

    • 每个线程在Java堆中预先分配一小块内存,然后在给对象分配内存的时候,直接在自己这块”私有”内存中分配,当这部分区域用完之后,再分配新的”私有”内存。

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

    什么是TLAB

    TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

    在这里插入图片描述

    注意到上面的描述中"线程专属"、“只给当前线程使用”、"每个线程单独拥有"的描述了吗?

    所以说,因为有了TLAB技术,堆内存并不是完完全全的线程共享,其eden区域中还是有一部分空间是分配给线程独享的。

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

    也就是说,虽然每个线程在初始化时都会去堆内存中申请一块TLAB,并不是说这个TLAB区域的内存其他线程就完全无法访问了,其他线程的读取还是可以的,只不过无法在这个区域中分配内存而已。

    并且,在TLAB分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被垃圾回收或者被移到Survivor Space、Old Gen等。

    在这里插入图片描述

    还有一点需要注意的是,我们说TLAB是在eden区分配的,因为eden区域本身就不太大,而且TLAB空间的内存也非常小,默认情况下仅占有整个Eden空间的1%。所以,必然存在一些大对象是无法在TLAB直接分配。

    遇到TLAB中无法分配的大对象,对象还是可能在eden区或者老年代等进行分配的,但是这种分配就需要进行同步控制,这也是为什么我们经常说:小的对象比大的对象分配起来更加高效

    TLAB带来的问题

    虽然在一定程度上,TLAB大大的提升了对象的分配速度,但是TLAB并不是就没有任何问题的。前面我们说过,因为TLAB内存区域并不是很大,所以,有可能会经常出现不够的情况。在《实战Java虚拟机》中有这样一个例子:

    比如一个线程的TLAB空间有100KB,其中已经使用了80KB,当需要再分配一个30KB的对象时,就无法直接在TLAB中分配,遇到这种情况时,有两种处理方案:

    1. 如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则直接在堆内存中对该对象进行内存分配。
    2. 如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则废弃当前TLAB,重新申请TLAB空间再次进行内存分配。

    以上两个方案各有利弊,如果采用方案1,那么就可能存在着一种极端情况,就是TLAB只剩下1KB,就会导致后续需要分配的大多数对象都需要在堆内存直接分配。

    如果采用方案2,也有可能存在频繁废弃TLAB,频繁申请TLAB的情况,而我们知道,虽然在TLAB上分配内存是线程独享的,但是TLAB内存自己从堆中划分出来的过程确实可能存在冲突的,所以,TLAB的分配过程其实也是需要并发控制的。而频繁的TLAB分配就失去了使用TLAB的意义。

    为了解决这两个方案存在的问题,虚拟机定义了一个refill_waste的值,这个值可以翻译为“最大浪费空间”。

    当请求分配的内存大于refill_waste的时候,会选择在堆内存中分配。若小于refill_waste值,则会废弃当前TLAB,重新创建TLAB进行对象内存分配。

    前面的例子中,TLAB总空间100KB,使用了80KB,剩余20KB,如果设置的refill_waste的值为25KB,那么如果新对象的内存大于25KB,则直接堆内存分配,如果小于25KB,则会废弃掉之前的那个TLAB,重新分配一个TLAB空间,给新对象分配内存。

    TLAB的相关参数

    TLAB功能是可以选择开启或者关闭的,可以通过设置-XX:+/-UseTLAB参数来指定是否开启TLAB分配。

    TLAB默认是eden区的1%,可以通过选项-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小。

    默认情况下,TLAB的空间会在运行时不断调整,使系统达到最佳的运行状态。如果需要禁用自动调整TLAB的大小,可以使用-XX:-ResizeTLAB来禁用,并且使用-XX:TLABSize来手工指定TLAB的大小。

    TLAB的refill_waste也是可以调整的,默认值为64,即表示使用约为1/64空间大小作为refill_waste,使用参数:-XX:TLABRefillWasteFraction来调整。

    如果想要观察TLAB的使用情况,可以使用参数-XX+PringTLAB 进行跟踪。

    总结

    为了保证对象的内存分配过程中的线程安全性,HotSpot虚拟机提供了一种叫做TLAB(Thread Local Allocation Buffer)的技术。

    线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,当需要分配内存时,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率

    所以,“堆是线程共享的内存区域”这句话并不完全正确,因为TLAB是堆内存的一部分,它在读取上确实是线程共享的,但是在内存分配上,是线程独享的

    TLAB的空间其实并不大,所以大对象还是可能需要在堆内存中直接分配。那么,对象的内存分配步骤就是先尝试TLAB分配,空间不足之后,再判断是在eden分配还是在老年代分配(JVM有一个大对象直接进入老年代的阈值,超出阈值的大对象直接在老年代分配内存)
    在这里插入图片描述
    本文的概述都是基于HotSpot虚拟机的,因为HotSpot虚拟机是目前最流行的虚拟机了,大多数默认情况下,我们讨论的时候也都是基于HotSpot的。

    展开全文
  • 线程内存

    千次阅读 2015-03-19 20:12:38
    在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象...
  • 3.默认执行线程数调整: 可以通过在启动脚本增加如下参数,可以指定默认线程池的最小值、最大值: -Dweblogic.threadpool.MinPoolSize=100 -Dweblogic.threadpool.MaxPoolSize=500   这些是使用SUN的JDK...
  • Java堆内存线程共享的?

    千次阅读 2020-03-10 10:17:00
    Java作为一种面向对象的,跨平台语言,其对象、...在JVM的内存结构中,比较常见的两个区域就是堆内存和栈内存(如无特指,本文提到的栈均指的是虚拟机栈),关于堆和栈的区别,很多开发者也是如数家珍,有很多书籍...
  • 线程内存分配问题

    千次阅读 2016-11-18 12:48:30
    public class Test { public static void main(String[] args) throws InterruptedException { //创建两个线程 分配堆内存空间 获取一个线程引用 ExecutorService executor=Executors.newFixedThreadPool(8);
  • 分析java 线程占用内存 ... 我们还将尝试证明过多的垃圾回收或Java空间的内存占用问题通常不是由真正的内存泄漏引起的,而是由线程执行模式和大量的短期对象引起的。 背景 正如您从我过去的JVM概...
  • 所有线程共享主内存,每个线程有自己的工作内存 refreshing local memory to/from main memory must comply to JMM rules 产生线程安全的原因 线程的working memory是cpu的寄存器和高速缓存的抽象描述:现在的计....
  • Java堆内存线程共享的!面试官:你确定吗?

    千次阅读 多人点赞 2020-03-10 10:06:26
    Java作为一种面向对象的,跨平台语言,其对象、...在JVM的内存结构中,比较常见的两个区域就是堆内存和栈内存(如无特指,本文提到的栈均指的是虚拟机栈),关于堆和栈的区别,很多开发者也是如数家珍,有很多书籍,...
  • printf “0x%x\n”线程PID: 0x431 // 将线程PID转换为 16进制,为后面查找 jstack 日志做准备 jstack 进程PID | vim +/十六进制线程PID - // 例如:jstack 1040|vim +/0x431 - 但是对于线上问题定位来说...
  • java线程内存可见性-java内存模型

    千次阅读 热门讨论 2015-11-22 13:34:50
    java内存模型规定 ...1.线程对共享内存的所有操作都必须在自己的工作内存中,不能直接从主内存中读取。 2.不同线程中无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
  • 多任务和高并发是衡量一台计算机处理器的能力重要指标之一。一般衡量一个服务器性能的...在讨论Java内存模型和线程之前,先简单介绍一下硬件的效率与一致性。 硬件的效率与一致性 由于计算机的存储设备与处理器的运算
  • Java多线程中的内存模型

    千次阅读 2017-05-14 19:35:19
    内存模型在Java中,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。局部变量(Local Variables),方法定义参数(Java语言规范称之为Formal Method Parameters)和异常处理器参数...
  • 线程共享指的就是可以允许被所有线程共享访问的一块内存,包括区,方法区和运行时常量池。  1. java区  java区在虚拟机启动时被创建,并且他在实际内存中是可以不连续的。java区是用于存储对象实例的一...
  • linux线程内存开销

    千次阅读 2018-12-10 17:21:07
    1.首先是线程自己栈,程序没设置过,就是默认的,ulimit -s 中的值,现在一般都是... 这个主要是用来解决,线程申请堆内存时,互相竞争的问题。每个线程优先在这个空间内申请堆空间 如何判断线程是否采用了 pe...
  • JVM堆内存与非堆内存(heap)官方详解

    千次阅读 2019-04-02 14:48:10
    JVM堆内存与非堆内存(heap)官方详解 JAVA堆内存管理是影响性能主要因素之一。 堆内存溢出是JAVA项目非常常见的故障,在解决该问题之前,必须先了解下JAVA堆内存是怎么工作的。 先看下JAVA堆内存是如何划分的,如图:...
  • JAVA 查看线程内存

    千次阅读 2019-09-10 10:51:51
    查看运行程序pid jps —— jdk 自带工具 与linux上的ps类似,用来显示本地的java进程,并显示他们的进程ID jconsole:一个java GUI监视...连接上 进程之后,将会展示如下窗口:可以看到具体的内存使用情况 window...
  • 主要功能:查看堆内存的分配状况;跟踪内存分配情况。 有助于我们找到较大的对象,及代码中耗内存和内存泄漏的地方,是分析优化代码内存使用有力工具。 使用方法: jps找到Java线程pid号 jmap -dump:format=b,file=...
  • *在指定的上分配内存 *获取 分配的内存块的大小 * 释放分配的内存 *销毁HeapCreate 创建的 */ #include #include using namespace std; void main() { //定义变量 HANDLE hHeap; //句柄 SYSTEM_...
  • C++编程中常见的线程内存问题

    千次阅读 2017-11-01 18:31:40
    最近在梳理一些的知识,这儿汇总一下C++中经常遇到的线程内存问题,以便后续注意。线程问题:丢失更新、脏读、死锁等 避免多线程使用线程不安全的函数 避免多线程读写的数据不加锁保护 避免相互调用的函数加成同一...
  • 线程内存管理

    千次阅读 2010-11-13 17:39:00
     假设有一个进程,创建了两个线程A、B,线程A在上分配了一块内存空间,通知传指针的方式在B中使用,使用完后释放块,这时就会出错,因为线程B不能释放线程A上的内存空间,一些网友对此也有一些看法。...
  • 线程的工作内存

    千次阅读 2017-04-21 14:48:39
    所谓线程的“工作内存”到底是个什么东西?有的人认为是线程的栈,其实这种理解是不正确的。看看JLS(java语言规范)对线程工作 内存的描述,线程的working memory只是cpu的寄存器和高速缓存的抽象描述。    ...
  • 关于多线程内存分配的问题

    千次阅读 2009-02-26 10:19:00
    by mayflowers Quote:资料上说User::Alloc 这个API 不是线程安全的(似乎除了继承RHandleBase的类是线程安全的其他有关内存的分配的API貌似都不是线程安全的)那么如果我用了多线程技术,并且这些线程共享了区...
  • 线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。 当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。 ....
  • 深入理解Java之JVM堆内存分配

    万次阅读 2017-04-20 22:16:56
    为了进行高效的垃圾回收,虚拟机把堆内存划分成新生代、老年代和永久代(1.8中无永久代,使用metaspace实现)三块区域。 Java把内存分成两种:栈内存和堆内存。关于堆内存和栈内存的区别与联系。简单的来讲,堆内存...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 319,592
精华内容 127,836
关键字:

线程堆内存