1 JVM的解析

1.1 JVM的结构图

        大多数 JVM 将内存区域划分为 Method Area(Non-Heap)(方法区) ,Heap(堆) , Program Counter Register  
        (程序计数器) ,   VM Stack(虚拟机栈,也有翻译成JAVA 方法栈的),Native Method Stack  ( 本地方法栈 )  
        ,其中Method Area 和  Heap 是线程共享的  ,VM Stack,Native Method Stack  和Program Counter Register     
        是非线程共享的。为什么分为 线程共享和非线程共享的呢?请继续往下看。

        首先我们熟悉一下一个一般性的 Java 程序的工作过程。一个 Java 源程序文件,会被编译为字节码文件  
        (以 class 为扩展名),每个java程序都需要运行在自己的JVM上,然后告知 JVM 程序的运行入口,再被 JVM 通过字  
        节码解释器加载运行。那么程序开始运行后,都是如何涉及到各内存区域的呢?  

        概括地说来,JVM初始运行的时候都会分配好 Method Area(方法区) 和Heap(堆) ,而JVM 每遇到一个线程,  
        就为其分配一个 Program Counter Register(程序计数器) ,   VM Stack(虚拟机栈)和Native Method   
        Stack  (本地方法栈), 当线程终止时,三者(虚拟机栈,本地方法栈和程序计数器)所占用的内存空间也会被释放  
        掉。这也是为什么我把内存区域分为线程共享和非线程共享的原因,非线程共享的那三个区域的生命周期与所属线程相  
        同,而线程共享的区域与JAVA程序运行的生命周期相同,所以这也是系统垃圾回收的场所只发生在线程共享的区域  
        (实际上对大部分虚拟机来说知发生在Heap上)的原因。

1.2 JVM更直观的功能导图

        方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信  
        息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都  
        来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的  
        大小时,会抛出OutOfMemory的错误信息  

1.3 JVM加载程序从编译到运行的过程

我将其总结为 5 点:1:编译 2:加载 3:实现 4: 初始化 5:执行main

    首先java程序在执行时会被编译成java字节码即.class文件————>其次加载配置文件 jvm.cfg(会根据系统的版本去寻找该  
    配置文件)————>根据加载后的配置文件找到 jvm.dll文件,该文件是 jvm 的主要实现 ————> 初始化java本地接口JNI接口  
    ————> 找到main方法,执行程序  

    注意JNI的作用:当一个程序被编译成java字节码之后是保存在硬盘中,而JVM是如何从硬盘中找到该.class文件,  

                  JNI接口的作用就是将该.class文件装载到JVM中,然后才能找到main方法执行程序,所以JNI接口的作用  
                  非常重要。 进而是如何装载.class文件的下面继续详细介绍  

1.4 JVM的内部图的详细介绍

    .class文件被加载之后,经过JVM的内存空间调度,最终由执行引擎完成对.class文件的执行  

内存空间

    JVM的内存空间包括: 方法区、java堆、java栈、本地方法栈。  

                       方法区是各个《《线程共享》》的区域,用于存放《《类信息、常量、静态变量》》  

                             java堆也是《《线程共享》》的区域,存放《《类的实例即对象》》,java堆的空间是最大的,空间不足时会  
                             抛出OutOfMemoryError异常  


                                    java栈是每个线程的私有区域,他的生命周期与线程相同,一个线程对应一个java  
                                    栈,每执行一个方法就会往栈中压入一个元素,这个元素叫 “栈帧” ,而栈帧中包含  
                                    了方法中的局部变量、用于存放中间状态值的操作栈。如果java栈空间不足了,程序会抛  
                                    StackOverflowError异常,  

                                    想一想什么情况下会容易产生这个错误,对,递归,递归如果深度很深,就会执行大量

                                    的方法,方法越多java栈的占用空间越大。

                                    每个帧代表一个方法,Java方法有两种返回方式,return和抛出异常,两种方式都会  
                                    导致该方法对应的帧出栈和释放内存。

                                    栈运行原理
                                    栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个  
                                    数据集,是一个有关方法和运行期数据的数据集,当一个方法A被调用时就产生了一个  
                                    栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,B方法  
                                    又调用了C方法,于是产生栈帧F3也被压入栈…… 依次执行完毕后,先弹出后  
                                    进......F3栈帧,再弹出F2栈帧,再弹出F1栈帧。
                                    遵循“先进后出”/“后进先出”原则。

                                    帧的组成:局部变量区(包括方法参数和局部变量,对于instance方法,还要首先保  
                                    存this类型,其中方法参数按照声明顺序严格放置,局部变量可以任意放置),操作  
                                    数栈,帧数据区(用来帮助支持常量池的解析,正常方法返回和异常处理)。   

                                        栈帧中主要保存3类数据:

                                            本地变量(Local Variables):输入参数和输出参数以及方法内的变量;

                                            栈操作(Operand Stack):记录出栈、入栈的操作;

                                            栈帧数据(Frame Data):包括类文件、方法等等。


                        本地方法栈角色和java栈类似,只不过它是用来表示执行本地方法的,本地方法栈存放的方法调用本  
                        地方法接口,最终调用本地方法库,实现与操作系统、硬件交互的目的。

                        PC寄存器,说到这里我们的类已经加载了,实例对象、方法、静态变量都去了自己改去的地方,那么问  
                        题来了,程序该怎么执行,哪个方法先执行,哪个方法后执行,这些指令执行的顺序就是PC寄存器在  
                        管,它的作用就是控制程序指令的执行顺序。

                        执行引擎当然就是根据PC寄存器调配的指令顺序,依次执行程序指令。    

静态变量+常量+类信息+运行时常量池存在方法区中,实例变量存在堆内存中。

基本类型的变量和对象的引用变量都是在函数的栈内存中分配。

2 GC的解析

2.1 垃圾回收与算法结构图

3 Spring相关易错点

3.1 Spring的事务

  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
  • PROPAGATION_REQUIRED:必须的。说明必须要有事物,没有就新建事物。
  • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
  • PROPAGATION_NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚

    mandatory:强制的。说明一定要有事务,没有事务就抛出异常。required_new:必须新建事物。如果当前存在事物就挂起。  
    not_supported:不支持事物,如果存在事物就挂起。never:绝不有事务。如果存在事物就抛出异常  

2 SpringMVC的原理

        客户端发送请求————>