JVM
JVM、JRE、JDK区别
JVM的作用
面试
理解底层的实现原理:字节码等等
中高级程序员的必备技能
JVM学习路线
程序计数器
Program Counter Register程序计数器(寄存器)
左边的是二进制字节码,这是跨平台的基础,都是jvm的指令。
还必须经过解释器,变成机器码,然后交给了CPU进行处理。
作用:
程序计数器的作用就是:记住下一条jvm指令 的执行地址。
等第一条指令在解释器解释完之后,3会放入到程序计数器中,解释器就会进去取,然后执行。就是记住下一条jvm指令的执行地址。
物理上,是通过寄存器实现的。寄存器是整个cpu里读取指令速度较快的。读取指令是非常频繁的,所以jvm中就把寄存器当做了程序计数器,进行jvm指令的执行地址的存放。
特点:
1、线程是私有的。
多个线程运行的时候,cpu会有一个调度器组件分配时间片,给线程1分配时间片,如果线程1没有执行完,就会把线程1 的状态暂存然后转到线程2去,开始执行线程2,然后等到线程2的时间片用完了,就开始转回到线程1去。如果恰好线程1执行到10(下一个要10,)记录到了程序计数器里面,要搞清楚程序计数器是私有的,是只属于线程1的,等开始转到线程2之后,就开始运行2的,然后转回1之后,就知道要开始运行10了,每个线程都有自己的程序计数器。
2、唯一一个不会存在内存溢出的区。
其他的一些区,堆栈、方法区等可能会存在内存溢出,而程序计数器不会存在内存溢出问题。
JVM Stacks 虚拟机栈
栈的概念就是类似弹夹,先进后出。
java中线程运行的时候,线程需要栈。栈:线程运行需要的内存空间。
栈是由栈帧组成。一个栈帧就对应一个方法的调用。
每个方法运行时需要的内存就是栈帧。
方法运行需要的内存:参数、局部变量、返回地址。都是需要占用内存的,所以每次方法运行时,需要预先分配好。
如果调用第一段方法时,把栈帧1放入,栈帧1调用了方法2(栈帧2,)然后栈帧2就放入了栈内。然后以此类推等到所有的调用都结束了,所有的栈帧都逐步出去。
定义
问题辨析:线程安全
1、垃圾回收是否涉及栈内存。
不涉及,垃圾回收只涉及堆内存。
2、栈内存分配越大越好吗?
不是,如果大了,会影响线程的数量,因为物理内存是有限的。
3、方法内的局部变量是否线程安全?
是。要观察这个线程是否对每个变量是否是私有的。局部变量是私有的。但如果把变量改成static成为了共享的就需要考虑线程安全,是私有的就不需要考虑线程安全。
一个线程对应一个栈,不同的线程会产生不同的栈帧,所以是安全的。
相当于每个线程都有自己的私有的栈帧。
这个sb是作为参数传进来,可能会有不同的线程同时在使用这个sb,已经不再是私有的了,所以要改掉。
这个sb虽然是局部变量,但是是返回了sb,所以可能会被其他线程并发的执行,所以总的来说,要看一个变量是不是线程安全的,那么就要看清楚是不是逃离了这个方法的作用范围,如果逃离了就不是线程安全的了。方法内局部变量始终在方法内的话,就是线程安全的。
问题辨析:栈内存溢出
第一种情况是:栈帧过多导致栈内存溢出。
方法递归调用没有设置正确的一个调用条件,就容易栈内内存溢出。
第二种情况是栈帧过大导致栈内存溢出。但这种情况比较少出现,一般是栈帧过多导致内存溢出。
错误名字是 stackoverflow。
可以提前设置好栈的总大小空间。
线程运行诊断
1、CPU占用过多。
2、程序运行很长时间没有结果