文章目录
- Java-JVM 角度说进程和线程之间的关系
- Java内存分配中的栈
- 参考
Java-JVM 角度说进程和线程之间的关系
一个进程可以有多个线程,多个线程共享进程的堆和方法区(JDK 1.8 之后的元空间)资源。但是每个线程有自己的程序计数器、虚拟机栈和本地方法栈**。
(1) 程序计数器为什么是私有的?
首先明确程序计数器的作用:
- 字节码解释器通过改变程序计数器来一次读取指令,从而实现代码的流程控制。如:顺序执行、选择、循环、异常处理。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程运行到哪了。
需要注意的是:如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。
所以,程序计数器私有主要是为了线程切换后能够恢复到正确的执行位置。
(2) 虚拟机栈和本地方法栈为什么是私有的?
- 虚拟机栈:每个Java 方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至完成的过程,就对应一个帧栈在 Java 虚拟机中入栈和出栈的过程。
- 本地方法栈:和虚拟机的作用非常相似。区别是:虚拟机为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 native 方法服务。在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。
(3) 堆和方法区
堆和方法区是所有线程共享的资源,其中堆是进程中最大的一块内存,主要用来存放新创建的对象(所有的对象都在这里分配内存);方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。
Java内存分配中的栈
栈帧
参考URL:
在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。
- 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中。
- 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
- 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧(Stack Frame),对应一次次方法的调用,栈的生命周期和线程一致,在一个时间点上,只有一个活动的栈帧,即当前栈帧,jvm对java栈的操作只有入栈、出栈。
作用:主管java程序的运行,保存方法的局部变量、部分结果,并参与方法的调用和返回。
java虚拟机规范允许java栈的大小是动态或者固定不变的
- 如果采用固定大小的java栈,那每一个线程的栈容量在线程创建的时候独立选定,如果线程请求分配的栈容量超过栈的最大容量,会抛出 StackOverflowError 异常
- 如果java栈是动态扩展的,在尝试扩展时无法申请到足够内存,或创建线程时没有足够的内存去创建对应的栈,会抛出 OutOfMemoryError 异常
栈内存大小设置
单位为(K,M,G),不区分大小写
--Xss256k
# java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
intx CompilerThreadStackSize = 0 {pd product}
intx ThreadStackSize = 1024 {pd product}
intx VMThreadStackSize = 1024 {pd product}
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
参考
Java 并发基础知识
参考URL: