大家好,感谢邀请,今天来为大家分享一下java虚拟机内存模型的问题,以及和java虚拟机如何安装的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
JAVA的内存是如何划分的
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间。Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如下图(图片来源网上)所示:
下面我们具体就每个场景进行阐述。
运行时数据区域方法区:方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区包含运行时常量池,是用于存放编译期生成的各种字面量和符号引用的。垃圾收集行为在这个区域比较少出现,但并非数据进了方法区就永久的存在了,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,当方法区无法满足内存分配需要时,将抛出OutOfMemoryError异常。虚拟机栈:Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链表、方法出口信息等。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。这块内存是线程私有的。本地方法栈:本地方法栈与虚拟机栈的作用相似,不同之处在于虚拟机栈为虚拟机执行的Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。会抛出stackOverflowError和OutOfMemoryError异常。这块内存是线程私有的。堆:堆是所有线程共享的一块内存区域,在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及其属性都在这里分配内存。堆是垃圾收集器管理的主要区域。由于现在收集器基本采用分代回收算法,所以堆还可细分为:新生代和老年代。如果堆中没有内存完成实例分配,并且堆也无法完成扩展时,将会抛出OutOfMemoryError异常。程序计数器:可以看做是当前线程所执行的字节码的行号指示器。这块内存是线程私有的。直接内存:直接内存不是虚拟机运行时数据区的一部分,在NIO类中引入一种基于通道与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。了解了运行时数据区域相关构成后,下面我们来看看对象的创建过程,如图:
我们着重看最右边一部分,不同的变量会分配到不同的内存区域里,比如new出来的东西会存放到堆中,而局部变量则是存放在栈中。
总之,了解并掌握这些知识是一个合格的Java程序员必需的技能,也只有这样才能写出更好的程序,更好地优化程序的性能。
如何理解Java虚拟机栈
和程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是Java方法执行的内存模型。
Java内存可以粗糙的区分为堆内存(Heap)和栈内存(Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。(实际上,Java虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。)
局部变量表主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
Java虚拟机栈会出现两种异常:StackOverFlowError和OutOfMemoryError。
StackOverFlowError:若Java虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。
OutOfMemoryError:若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。
Java虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。《深入理解Java虚拟机》过时了吗
目前的情况远远没到过时的程度!
如果是从事Java开发,我觉得早点深入理解Java虚拟机一定是正确的选择,这种核心基础的功力如果修炼的好,一定会对工作甚至职业有很大的帮助!!
jmm内存模型详解
JMM(Java内存模型)是Java中定义的一种规范,用于描述多线程环境下,线程与主内存之间的交互和数据共享方式。JMM主要有以下几个方面的内容:
1.主内存(MainMemory):主内存是Java内存模型中的一个概念,它是所有线程共享的内存区域。主内存存储了所有的变量、对象实例以及执行结果等。
2.工作内存(WorkingMemory):每个线程都有自己的工作内存,工作内存是线程对主内存中的变量副本的私有拷贝。线程在使用变量时,首先需要将变量从主内存中拷贝到工作内存,然后对工作内存中的变量进行操作。
3.内存间交互操作:JMM定义了一系列规则来控制线程与主内存之间的交互操作,包括以下操作:
-read(读取):线程从主内存中将变量的值读取到工作内存中。
-load(载入):将read操作得到的变量值放入工作内存的变量副本中。
-use(使用):线程使用工作内存中的变量副本进行计算、赋值等操作。
-assign(赋值):将工作内存中变量的值赋给主内存中的对应变量。
-store(存储):将assign操作得到的变量值写回主内存的变量中。
-write(写入):线程将变量的值写入到主内存中。
4.happens-before关系:JMM定义了happens-before关系,该关系用于确定不同线程操作之间的顺序。如果一个操作happens-before另一个操作,那么第一个操作的结果对于第二个操作是可见的。happens-before关系能够保证程序在多线程环境下的执行顺序的一致性和可预测性。
Java内存模型通过上述规范来保证多线程环境下的数据可见性、原子性和有序性。开发者可以利用JMM提供的特性和规则来编写正确且线程安全的多线程程序,避免出现数据竞争、死锁等问题。
java的堆内存是如何被回收的
以常见的CMS收集器进行说明,首先会对堆划分为年轻代和年老代
Young区(年轻代),下面的默认的比例是8:1:1Edge区ToSurvivor区FromSurvivor区Old区(年老代)对象优先分配在年轻代的Edge区,(如果对象过大,可以直接在old区分配,通过jvm参数可以设置这个阈值)
当Edge区塞不下,就需要回收空间腾地方(即触发一次minorGC),(注意此时ToSurvivor是空的,FromSurvivor有存活对象)采用的原则是:
-将Edge区和FromSurvivor区存活的对象塞到ToSurvivor区
-完事之后,表示清空FromSurvivor和Edge中需要回收的对象,此时FromSurvivor就变成了新的ToSurvivor区
看到这里一个问题就来了,如果ToSurvivor也塞不下这些数据怎么办?
此时就需要把Survivor区的对象塞到Old区了,如果Old区也塞不下(没有足够大的连续空间来存储这些对象),在会触发FullGC,FullGC之后还塞不下,那就oom了
所以从上面这个流程中,可以得出一个结论FullGC必然伴随一次MinorGC
额外说一句,内存分配和回收策略的内容并不只是上面这一点,建议看下广受好评的《深入理解Java虚拟机-JVM高级特性与最佳实践》
关于java虚拟机内存模型到此分享完毕,希望能帮助到您。