JVM 内存分析

简述JVM垃圾回收机制

  1. 垃圾回收机制时Java提供的自动释放内存空间的机制.
  2. 垃圾回收机制时JVM自导的一个线程,用于回收没有被引用的对象.

JVM有一个运行时的数据区来管理内存.其主要包括五大部分:程序计数器,虚拟机栈,本地方法栈,方法区,堆.

其中程序计数器,虚拟机栈,本地方法栈 每个线程私有的内存空间,和线程的生命周期相同.如栈中每个栈帧分配多少的内存基本上在类结构确定时就以几个确定了.无需考虑内存回收的问题.

方法区和堆就和上面不一样了,一个接口的多个类实现需要的内存可能不一样,只有在程序运行期才指导会创建哪些对象,这部分内存的分配和回收都是动态的, GC主要关注的就是该部分内存.

Java虚拟机数据区域

黄色: 由所有线程共享的数据区.

绿色: 线程隔离的数据区.


  1. 程序计数器:当前线程所执行的字节码的行号指示器,在虚拟机的概念模式里,字节码解释器工作就是通过改变程序计数器的值来选择下一跳需要执行的字节码指令,分支,循环,跳转,异常处理,线程回复等基础功能都要依赖这个基础器来完成.

Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,指挥执行一条线程中的指令.因此,为了线程切换之后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线成之间的计数器互不影响,独立存储.

如果线程正在执行一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是Native方法,则这个计数器值为空,此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域.


  1. Java虚拟机栈:其也是线程私有的,生命周期和线程相同.其永固Java方法执行的内存模型:每个方法在执行的时候都会创建一个栈帧用于存取局部变量表,操作数栈,动态链接,方法出口等信息.

3.本地方法栈:其也是线程私有,描述的是Native方法执行的内存模型.虚拟机规范对本地方法栈中方法所使用的语言,使用方式和数据结构并没有强制规定.


  1. Java堆:线程共享,所有的对象实例和数组都要在堆上分配内存.Java堆是垃圾手机管理的主要区域,由于现在收集器基本采用分代回收算法,所以Java堆还可以细分为:新生代和老年代.从内存分配的角度上看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区.如果队中没有内存完成实力分配,并且堆无法完成扩展,将会抛出OutOfMemoryError的错误.

  1. 方法区:线程共享,存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据.此处的垃圾回收主要针对,常量池的回收和对类型的加载.

运行时常量池:方法区的一部分,它用于存放编译器生成的各种字面量和符号引用.


  1. 直接内存:直接内存不是虚拟机运行时数据区的一部分,在NIO类中引入一种基于通道与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java对中的DirectByteBuffer对象作为这块内存的引用进行操作.



对象的创建


  • 之前没有加载过的情况下
  1. 类加载,同时初始化类中的静态的属性和方法.(所有带static修饰符的属性,方法和静态代码块都放在内存中的方法区,而非静态属性放在堆区.)
  2. 执行静态代码块(主动执行. 此时静态方法已经在方法区中,就算不实例化对象也可以直接用类区调用静态方法了, 而在此之后才开始初始化的属性和方法是不可以在其中被调用执行的(例如非静态方法和属性).)
  3. 分配内存空间(此时是开始new 对象了.在堆中开始开辟大小足够的空间,然后将地址赋予占内存中以便引用.放在堆内存中的时候,各种属性赋予默认值(0和null).).
  4. 调用父类构造器
  5. 自己的声明属性有显示赋值则开始将,显示赋值覆盖默认赋值.
public class test {                         //1.第一步,准备加载类
    public static void main(String[] args) {
        new test();                         //4.第四步,new一个类,但在new之前要处理匿名代码块        
    }
    static int num = 4;                    //2.第二步,静态变量和静态代码块的加载顺序由编写先后决定 
    {
        num += 3;
        System.out.println("b");           //5.第五步,按照顺序加载匿名代码块,代码块中有打印
    }
    int a = 5;                             //6.第六步,按照顺序加载变量
    { // 成员变量第三个
        System.out.println("c");           //7.第七步,按照顺序打印c
    }
    test() { // 类的构造函数,第四个加载
        System.out.println("d");           //8.第八步,最后加载构造函数,完成对象的建立
    }
    static {                              // 3.第三步,静态块,然后执行静态代码块,因为有输出,故打印a
        System.out.println("a");
    }
    static void run()                    // 静态方法,调用的时候才加载// 注意看,e没有加载
    {
        System.out.println("e");
    }
}

无用类的判定:

  1. 该类的所有实例都已经被回收,也就是堆中不存在该类的任何实例.
  2. 类加载器已经被回收.
  3. 该类对应的对象没有在任何地方被引用,也就是说这东西你再也没办法用了.

原文地址:https://www.cnblogs.com/A-FM/p/10917111.html