JUC与JVM(5)本地方法栈/java栈/寄存器
native method stack本地方法栈/本地方法接口/本地方法库
问题引出:
运行以下代码
public static void main(String[] args) throws ClassNotFoundException {
Thread t1 = new Thread();
t1.start();
t1.start();
}
java.lang.IllegalThreadStateException
原因参加源码:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
当threadStatus!=0时,会抛出异常;故可知java中,每个Thread对象,必须保证threadStatus=0时才能启动,否则会抛出异常;
同时观察到 try{}内部有个start0();方法;
private native void start0();
查看源码知,刚方法有关键字native;
当方法中有native关键字,即说明该方法超出了java的领域,需要操作系统的资源或者其他语言(C语言等)的库来实现;
而这些native方法的运行即放在本地方法栈中(native method stack);类似于绿色通道的存在;
而本地接口,初衷时为了融合C/C++程序,在内存中专门开辟一块区域处理标记为native的代码;具体做法:native method stack 中登记标记了native方法,在execution engine 执行时,加载native libary;
目前该方法使用越来越少,除非和硬件下相关的应用,因为现在异构领域间的通信很发达,如可以使用stock通信,webservice等;
pc寄存器
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法去中的方法字节(用来储存指向下一条指令的地址,也即将要指向的代码指令),由执行引擎读取下一条指令,是一个非常小的内存空间;
这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条所需执行的字节码指令;
如果执行的native方法,这个计数器是空的;
用来完成分支,循环,跳转,异常处理,线程恢复等基础功能,不会发生内存溢出(OOM)错误;
方法区
所有程序线程共享;存在垃圾回收机制
描述:
供每个线程共享的运行时的内存区域,它存储了每一个类的结构信息,例如运行时的常量池(Runtime constant pool),字段,方法数据,构造函数,普通方法的字节码内容;
方法去这是规范,在不同虚拟机,实现是不一样的,最典型的就是永久代(permgen space)和元空间(Metaspace);
JDK1.7以下永久代,JDK1.8即以上元空间;
####类加载器的图:
内存空间区域如下:
上图的内存区域的变化如下:
class file(硬盘中)----ClassLoader---->大Class对象,字节码对象(方法区中)-----实例化---->堆内存中
Java栈(Stack)
首先栈是一种数据结构:https://blog.csdn.net/qq_32193775/article/details/103947914
在java的jvm中,栈管运行,堆管存储;
描述: 栈也叫栈内存,主管java程序的运行,是线程创建时创建的,它的生命周期是跟随线程的生命周期的;线程结束栈内存空间释放;对于栈来说:不存在垃圾回收机制;只要线程结束,栈就结束,栈是线程私有的;8中基本类型的变量+对象的引用+实例方法都是在栈内存中;
栈存储什么?
- 本地变量:输入输出参数,以及方法中的变量
- 栈操作:入栈和出栈
- 栈帧数据:包括类文件,方法等;
方法=栈帧:如先运行main方法,即栈1,后序方法,栈2,栈3;每个入栈的方法即该栈的一个栈帧;
栈的运行原理:
栈中的数据都是以栈帧(stack frame)的格式存在。栈帧是一个内存区块。是一个数据集,是一个有关方法和运行期间的数据集,
- 当一个方法A被调用就产生了一个栈帧F1,压栈
- A方法调用方法B,产生栈桢F2,压栈
- B方法调用方法C,产生栈桢3,压栈
- 执行完毕,弹出F3,后F2,再弹出F1;遵循先进后出的原则;
每个方法执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每个方法从调用直至执行完毕的过程,就对应着一个栈帧从虚拟机入栈到出栈的过程,栈的大小和具体JVM的实现有关,通常256K~756K之间;
栈溢出SOF
public static void main(String[] args) throws IOException {
m1();
}
private static void m1() {
m1();
}
Exception in thread "main" java.lang.StackOverflowError
问题:
StackOverflowError属于异常还是错误?
答案:错误
查看api层级架构
Throwable–>Error–>VitrualMachineError—>StackOverflowError
当时该错误,可能能够解决,多为递归时出现
堆,栈,方法区三者之间的关系
-
HotSpot是使用指针的方式来访问对象的;
- JDK的jvm名字就叫hotspot;hotspot有sun公司实现
- 实现接口中所有方法,就可以创作自称一派的JVM,目前主要5派
- oracle收购了sun和Ba公司,拥有两套JVM的规范,将这两套规范合二为一后,产生了JDK1.8;
- J9 JDK,Ali的JDK
- JDK的jvm名字就叫hotspot;hotspot有sun公司实现
-
Java堆中会存放访问类元数据的地址
- 元数据:描述数据的数据,此时即类的结构信息
-
reference存储的就直接是对象的地址