JVM虚拟机各个内存作用及分布+常见面试题

进程的概念:

进程就是一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序是一个进程从创建、运行到消亡的过程。

进程的概念:

线程和进程相似,一进程在执行过程中可以产生多个线程、于进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈、本地方法栈,所以系统在产生一个线程,或者在多个线程中切换工作时会比在进程中轻松的多,所以线程也叫轻量级的进程。

记住一句话:java程序天生就是多线程程序main线程和多个其他线程,不然也不会弄那么多技术优化去解决线程同步问题。

面试题:

从JVM角度说进程和线程之间的关系?

从图片不难看出来:

线程内部共享的:虚拟机栈、本地方法栈、程序计数器(PC)

线程共享的:堆、方法区、直接内存

 

下面对各个内存区域进行解释:

1、程序计数器(随线程同生共死):不会出现OutOfMemoryError

**程序计数器是一快较小的内存空间、可以看作是当前线程执行的字节码的行号指示器。当字节码解释器在工作时、通过程序计数器去执行指向下一行指令或者分支、循环、跳转等等功能、另外在线程切换后,为了保证每个线程的正常工作,在每个线程内部都要有一个程序计数器用于指向正确执行的位置。

2、java虚拟机栈:(随线程同生共死):描述java方法执行的内存模型

java内存可以粗糙的分为堆内存、栈内存、其中说的栈就是虚拟机栈,或者说虚拟机栈中局部变量表部分。(实际上,java虚拟机栈是由一个一个栈帧组成,每个栈帧都拥有局部变量表、操作数栈、动态链接、方法出口信息)。

局部变量表主要存放了编译器可知的各种数据类型(byte、short、int、long、float、double、char、boolean)、对象引用,它不同于对象本身,指向的是对象起始地址、也可能指向对象句柄或对象的其他位置

会出现的异常:StackOverFlowError、OutOfMemoryError。

3、本地方法栈:(随线程同生共死)

和虚拟机栈所发挥的作用非常相似、区别是:虚拟机栈为虚拟机执行java方法(字节码)服务、而本地方法是为执行Native方法服务。在HotSpot虚拟机中和java虚拟机栈合二为一。

本地方法创建后、在本地方法栈中也会创建一个栈帧用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会抛出:StackOverFlowError、OutOfMemoryError错误。

4、堆:

Java虚拟机所管理的内存中最大的一块,所有线程共享的一快内存区域、在虚拟机启动创建时创建,作用存放对象实例、几乎所有对象实例和数组都是在这里分配内存。这里是垃圾收集器GC管理的主要部分,从垃圾收集器的角度来看,现在垃圾收集器都按代划分,所以堆还可以细分为,新生代和老年代:再细致一点有:Eden空间、From Surivivor、To Survivor空间等。进一步划分的目的是更好的回收内存,或者更好地分配内存。

在jdk1.8后取消了永久代(堆内存空间),取而代之的是元空间(物理内存)。

方法区:

用于存储java虚拟机加载的类信息、静态变量、常量、即使编译器编译后的代码等数据。虽然java虚拟机将方法区描述为堆的一部分。但是它也有一个别名(NON-Heap)非堆,用于把它和堆区分开来。

在HotSpot虚拟机中将方法区归为永久代中,这样并不是它就是永久代,是为了让他在垃圾回收时一同管理这部分区域。但是这并不是好的主意。因为这样常常会照成内存的溢出。相对而言,垃圾收集行为在这部份很少出现,但并不是说它进去了方法区就永远不会被清除。

运行时常量池:

这部分是方法区的一部分,.class字节码文件不止是由类的版本、字段、方法、接口等描述信息、还会有常量池信息(用于存放编译时生成的各种字面量和符号引用。

既然运行时常量池是方法区的一部分,那么它必定会受方法区的限制,当常量池无法再申请到内存时就会抛出OutOfMemoryError异常。

jdk1.7及以后版本的JVM将常量池从方法区中移除,并且在堆中开辟了一快空间,用于存放运行时常量池。

直接内存:

直接内存并不是虚拟机运行时数据区的一部份、也不是虚拟机规范中定义的内存区域,但是这部分内存也会被频繁使用。并且也会导致OutOfMemoryError.

在JDK1.4后加入了NIO(New Input/Output)类,一种基于通道和缓存区的I/O方式。他可以直接使用Native函数库直接分配堆外内存。也可以通过java堆中的DirectBuffer对象作为这个快内存的引用进行操作。这样就能在一些场景中显著提升性能。避免了java堆和Native堆来回复制内容。

虽然这个是不受java堆的限制,但毕竟是内存,所以回受本机内存限制。

Java创建对象的过程:

类加载检查--->分配内存----->初始化值------>创建对象头----->执行init方法

java内存分配的两种方法:

指针碰撞(堆内存分配整齐)    空闲列表(堆内存分配不整齐)

内存分配的并发问题:

针对于内存分配的并发问题JVM肯定对应付线程安全问题有处理方法:

*CAS+失败重试:CAS是乐观锁的一种实现方式,就是不上锁,每次都以为是没有冲突去执行这些操作。知道成功为止。虚拟机采用CAS配上大量失败重试方式保证了原子性。

*TLAB:给每个线程在堆内存中的Eden区分配一块内存,当JVM给线程分配内存时首先在TLAB中分配。如果对象内存太于TLAB中内存,那么就会再利用CAS给对象分配内存。

对象再内存中的布局:对象头(hash值、锁状态等     加指向类元素的指针)、实例数据、和对齐填充。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值