深入理解JVM内存模型

1、程序计数器在虚拟机的概念模型里字节码解释器工作时就是通过改变

这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、

Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现

的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行

一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要

有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内

存区域为“线程私有”的内存。

线程恢复等基础功能都需要依赖这个计数器来完成。

2、线程栈VM栈的默认大小是1M。用于在调用函数时持有局部变量并维护状态的内存区域

-Xss 128k:设置每个线程的堆栈大小。不能无限生成,经验值在3000~5000左右

线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时
时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会
出现内存溢出的错误.

异常 :Fatal: Stack size too small

异常的引起一般是线程数目太多

3、本地方法栈

即为一些Native方法分配的stack

Native方法,java调用C程序

在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

  1.为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问

  2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的

  3.为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

4、栈每个线程的栈都是该线程私有的,堆则是所有线程共享的,这里说的堆,主要指程序能控制的,包括

新生代:The New Generational Heap,默认4M,此区域一般为JVM内存的1/15大小。此代分为Eden space区,Survivo space区可以看成emptySurvivo区,Survivor区,

每次算法开始都得停止当前所有的线程,然后把Survivor区的所有活跃的对象复制到emptySurvivo区,然后对Survivor区空间进行清除变成emptySurvivo,以前的emptySurvivo成为了Survivor区。(互换)

当new 一个对象时,首先是在Eden space区,当Eden space区满时,Survivor space区进行垃圾回收(此处复制算法),当对象在Survivo space区经过几次回收Tenured Generation

老年代:此处储存年老代的对象,此处的垃圾回收采用The Mark and Sweep 算法

5、垃圾回收算法: 标记-清除算法从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片!

复制算法:根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间用于进行对象的移动。也就是我们前面提到的
s0 s1等空间。(Survivor 0和Survivor 1(这个估计是old Generation区?))

标记整理算法:采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。

对于新生代内存的回收(minor GC)主要采用复制算法

分代收集分代收集是利用程序有大量临时对象的特点,对象每被引用一次,代数就增加,代数小的小型对象会被回收整理,大对象只会代数增加,不会被整理。

  引用计数:当引用连接到对象时,对象计数加1,当引用离开作用域或被置为null时减1。遍历对象列表,计数为0就释放

6、垃圾回收器:因为GC主要就是对堆的回收,当然还有常量池和永久代

a、单线程程收集器serial collector:Stop The World垃圾回收器

Client下默认方式串行回收方式适合低端机器,是Client模式下的默认收集器,对CPU和内存的消耗不高,适合用户交互比较少,后台任务较多的系统。

b、并行收集器parallel collector

多线程版本的Serial收集器他是多CPU模式下的首选回收器

c、CMS又称响应时间优先(最短回收停顿)的回收器,使用并发模式回收垃圾,使用标记-清除算法,CMS对CPU是非常敏感的,它的回收线程数=(CPU+3)/4,因此当CPU是2核的实惠,回收线程将占用的CPU资源的50%,而当CPU核心数为4时仅占用25%
分四步骤:在初始标记的时候,需要中断所有用户线程,在并发标记阶段,用户线程和标记线程并发执行,而在这个过程中,随着内存引用关系的变化,可能会发生原来标记的对象被释放,进而引发新的垃圾,因此可能会产生一系列的浮动垃圾,不能被回收。

CMS在旧生代内存到达全部容量的68%就触发了CMS的回收!

d、G1收集器

商用高性能垃圾回收器,通过重新划分内存区域,整合优化CMS,

7、当然还一种程序调用system.gc()

调用System.gc()也仅仅是一个请求。JVM接受这个消息后,并不是立即做垃圾回收,而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。

8、垃圾回收的触发

new generation来说,当eden区的对象空大于Survivor0(假设为from)的free空间时,会发生minor gc,即对整个Survivor0区,Survivor1区进行一个复制算法垃圾回收,并且部分对象转到Old Generation

当new generation满了(即eden区+Survivor0区大于Old Generation的free空间时),将发生major gc,即对New Generation和Old Generation两代都进行垃圾回收。

所以得出,如果JVM的设置内存过大,发生GC的回收频率将越小,但是回收的时间越长(特别对new generation进行复制算法的复制时是需要停止当前所有线程的),所以并不是说JVM的内存设置的越大越好,得根据实际情况进行优化。

异常:异常 java.lang.OutOfMemoryError: Java heap space

一般是由于垃圾回收后,old generation里空间也不够用了。

9、堆内存的分配

-server时最大堆内存是物理内存的1/4,但小于1G

client 小一倍

参数:-Xms128m
表示JVM Heap(堆内存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆内存)最大允许的尺寸256MB,按需分配

new Generation与Old Generation的比例,默认为1:2,即为2

-XX:NewRatio= 参数可以设置(也可以-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值)

Eden与Survivor的比例,默认为32

-XX:SurvivorRation=参数可以设置

当对象默认经过1次New Generation 就转入Old Generation(这个不同文章上不同,待我确定)

10、方法区、永久栈Perm

其实也可与看成堆内存的一部分,看成永久代(Perm Space),但GC也会回收,但很少回收,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,比如一个int x,在不同的环境中是不同的,此信息就存在方法区

XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 设置垃圾不回收

-server选项下默认MaxPermSize为64m
-client选项下默认MaxPermSize为32m

11、常量池

编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放

GC的作用主要是用来卸载类和回收常量池,当然有部分方法区,即使永久代(Perm Space)也会一定的回收运行期间也可将新内容放入常量池(最典型的String.intern()方法)

12、所有线程共享主内存
每个线程有自己的工作内存。。每个线程都有自己的执行空间(即工作内存),线程执行的时候用到某变量,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作:读取,修改,赋值等,这些均在工作内存完成,操作完成后再将变量写回主内存;

多个线程同时读写某个内存数据时,就会产生多线程并发问题,涉及到三个特 性:原子性,有序性,可见性。

时间: 2024-08-29 13:27:11

深入理解JVM内存模型的相关文章

深入理解JVM(一)——JVM内存模型

JVM内存模型 Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 1. 程序计数器 2. Java虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区. 下面对这五个区域展开深入的介绍. 1. 程序计数器 1.1. 什么是程序计数器? 程序计数器是一块较小的内存空间,可以把它看作当前线程正在执行的字节码的行号指示器.也就是说,程序计数器里面记录的是当前线程正在执行的那一条字节码指令的地址. 注:但是,如果当前线程正在执行的是一个本地方法,那么此时程

深入理解JVM JVM内存模型

1.JVM内存模型        说起JVM内存模型,都是知道是Java方法区.Java栈.Native方法区.Java堆和程序计数器五部分,不过具体是做什么的,又有什么关系可能大家就不太清楚了,所以话不多说,直接上干货. 首先是JVM内存规范. 编译器和类加载在上篇博客已经讲了,不了解的可去看一下.现在主要就是运行时数据区了.具体请看下图 先给简单介绍一下什么是堆.桟.方法区以及格子 首先就是堆与栈分开设计是为什么呢? 栈存储了处理逻辑.堆存储了具体的数据,这样隔离设计更为清晰堆与栈分离,使得

JVM内存模型理解

Java虚拟机(Java VirtualMachine 简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一. JVM内存模型 1.方法区和堆是所有线程共享的数据区 1)堆:存放对象的实例 2)方法区:存放已被虚拟机加载的类信息.常量.静态变量.即时编译器编译后的代码 3)运行时常量池:是方法区的一部分,存放Class的版本.字段.方法.接口等描述信息,字符串池就是这个!!! 2.程序计数器.虚拟机栈.本地方法栈是线程隔离的数据区 4)程序

jvm的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集(转)

深入Java虚拟机:JVM中的Stack和Heap(转自:http://www.cnblogs.com/laoyangHJ/archive/2011/08/17/gc-Stack.html) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题. 一般,JVM的内存分为两部分:Stack和Heap. Stack(栈)是JVM的内存指令区.Stack管理很简单,push

【转】理解JVM内存区域

引言 对于C++程序员,内存分配与回收的处理一直是令人头疼的问题.Java由于自身的自动内存管理机制,使得管理内存变得非常轻松,不容易出现内存泄漏,溢出的问题. 不容易不代表不会出现问题,一旦内存泄漏或溢出的情况发生,调试起来会变得非常困难.这就要求我们对虚拟机的内存区域有深入的理解.最终能够判断内存方面的异常发生时,具体在JVM中的位置. 内存区域 JVM运行时,首先需要类加载器(ClassLoader) 加载所需类的字节码,加载完毕交由执行引擎执行,执行过程中需要一段空间来存储数据(类比CP

jvm内存模型及分配

1.什么是jvm?(1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的.(2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域.(3)JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行. 2.jdk.jre.jvm是什么关系?(1)JRE(Java

JVM内存模型及垃圾回收机制

JVM内存模型1.栈Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程.存储局部变量.引用.方法.返回值等.StackOverflowError:如果在线程执行的过程中,栈空间不够用,那么JVM就会抛出此异常,这种情况一般是死递归造成的.2.堆 Java中堆是由所有的线程共享的一块内存区域,堆用来保存各种JAVA对象,比如数组,线程对象等. 2.1堆的分代JVM堆一般分为三个部分:Young:年轻代Young区被划分为三部分,Eden区和两个大小严格相同的Su

深入理解java内存模型

深入理解Java内存模型(一)——基础 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信. 同步是指程序用于控制不同线程之

( 转)深入理解java内存模型系列

深入理解Java内存模型(一)——基础 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信. 同步是指程序用于控制不同线程之