线程共享指的就是可以允许被所有线程共享访问的一块内存,包括堆区,方法区和运行时常量池。
1. java堆区
java堆区在虚拟机启动时被创建,并且他在实际内存中是可以不连续的。java堆区是用于存储对象实例的一块内存,同时也是垃圾回收器执行的重点区域,因为堆区是垃圾回收器的重点回收区域,所以gc极有可能会在大内存的使用和回收工作上称为性能瓶颈。为了解决这个问题,JVM考虑是否一定会将对象的实例存储到java堆区内。
基于OpenJDK,深度定制的TaoBaoVM,其中创新的GCIH技术实现将生命周期较长的对象从堆区移动到堆外,并且垃圾回收器不可以处理这块内存,以此降低垃圾回收器的回收频率和提升垃圾回收器的目的。在某些特殊的应用中有许多很大的对象而且生命周期也很长,那么他们的存在必定会给垃圾回收器造成很多不必要的工作负担。假设淘宝有许多大量的重复对象,据说对象超多数百MB。他们本身在应用提供服务前创建,在服务过程中永远存在,那么实际垃圾回收器对这块内存做的收集就没用,如果我们将这些对象移动到堆外那么他们之前占据的堆就会不用那么每次FULLGC时间就会减少,GCIH为在jvm间共享对象提供了必要的基础。通过该技术可以实现对象的共享,从而减少了内存的总体占用量。除此之外,逃逸分析与栈上分配等优化技术也是降低了垃圾回收器,这样一来java堆区不在是java对象分配的唯一地方了。
存储在jvm中的java对象可以划分为两类,一类是生命周期较短的对象,该类对象创建和消亡非常快速,相反另外的一类对象生命周期非常长,在某些极端情况下可能和虚拟机生命周期一样长,因此对于这些不同的对象应该采取不同的回收方法。目前所有的垃圾回收都是采用分代算法,所以在堆区应该在划分为新生代和老年代,新生代又分为Eden 和 FROM Survivor和To Survivor。既然java堆区用于存储java对象的实例,那么堆的大小在虚拟机启动时就设定好啦,可以通过Xms设置最小堆区 Xmx最大堆区。一旦堆区的内存大小超多最大堆区会抛出OutOfMemoryError
2,方法区
方法区和堆区一样是允许被所有线程共享访问,方法区存储了每一个java类的结构信息,比如运行时常量池,字段和方法数据,构造函数和普通方法的字节码内容以及类,实例,接口初始化时需要的特殊方法等数据,尽管虚拟机规范对方法区的具体实现的方法并没有就具体说明但是在HotSpot中,方法区仅仅只是逻辑上的独立,实际还是被包含在java堆区内,也就是说物理上属于java堆区的一部分。
方法区在虚拟机启动时被创建,并且它的实际的内存空间和java堆区一样可以是不连续的。方法区是一块比较特殊的运行时内存区,有一些开发人员更愿意将方法区称为永久区,这是主要因为方法区除了可以通多选项-XX MaxPermSize设置内存大小进行动态扩除外,并不会像java堆区那样频繁的被gc执行回收,设置还可以显式的制定是否需要在程序运行时回收方法区中的数据。如果没有显式要求不对方法区内存回收的情况,gc的回收目标仅针对方法区中的常量池信息和类型卸载。方法区也可能出现OutOfMemoryError。条件是出现内存大小超多-XX MaxPermSize
3.运行时常量池属于方法区的一部分,也可能OutOfMemoryError。
线程私有内存
1.pc寄存器
由于虚拟机是基于栈的结构,所有任何的操作都需要入栈和出栈,虚拟机的pc寄存器并非广义的物理寄存器,应该是pc寄存器,pc寄存器是对物理pc寄存器的一种抽象模拟,他是线程私有的,生命周期与线程的生命周期一样,
如果当前线程所执行的是一个java方法,那么寄存器机会存储正在执行的字节码指令地址,反之如果是本地方法那么就是指向null,就是寄存器值是空未定义。
寄存器为什么设计为线程私有?
我们知道所谓的多线程在一个指定的时间段里只会执行某一个线程的方法,CPU不停切换任务,那么为了能够准备的记录各个线程正在执行的当前的字节码指令的地址,那么就设计为线程私有每个线程都分配一个寄存器,这样各个线程独自记录不会干扰。这是java虚拟机唯一一个没有声明OutOfMemoryError的内存区。
2.java栈
在虚拟机规范中,线程私有,生命周期与线程的生命周期一样.用于存储栈帧,而栈帧就是存着局部变量表,操作数栈,以及方法出口信息等.
java堆区既然存在的是对象实例,那么栈区中的局部变量表就是用于存储个类原始数据类型,对象引用,以及返回类型.如果递归深度过深会抛出OutOfMemoryError
3,本地方法栈
这块用c++实现