JVM相关学习

一、了解JVM

  1. JVM就是Java虚拟机。
  2. 好处:
  • 一次编写,到处运行;
  • 自动内存管理,垃圾回收功能;
  • 数组下标越界越界检查;
  • 多态。

二、学习JVM

1.程序计数器(寄存器): 

  • 作用:记住下一条jvm指令的执行地址;
  • 特点:

          (1)线程私有;

          (2)不会存在内存溢出(唯一一个不会内存溢出的区域);

2.栈和栈帧:

  • 栈:线程运行时需要的内存空间;
  • 栈帧:每个方法运行时需要的内存(包括参数,局部变量,返回地址等);
  • 一个栈由多个栈帧组成,对应着每次方法调用时所占用的内存,遵循先进后出的方法;
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
  • 注意:

                (1)垃圾回收不涉及栈内存,涉及堆内存;

                (2)栈内存的分配并不是越大越好;

                (3)方法内的局部变量是否线程安全?

                                a.如果方法内局部变量没有逃离方法的作用范围,它是线程安全的;

                                b.如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全。

2.2栈内存溢出:

  •  栈帧过多导致栈内存溢出(例如方法的递归调用,没有设置正确的终止条件);
  • 栈帧多大导致栈内存溢出;

2.3线程运行诊断:

  • CPU占用过高:
  1. 定位:用top定位哪个进程对CPU占用过高;
  2. ps H -eo pid, tid, &cup | grep 进程id(用ps命令进一步定位是哪个线程引起的cpu占用过高);
  3. jstack 进程id,可以根据线程id找到有问题的线程,进一步定位到问题代码的源码行号;
  • 程序运行很长时间没有结果:
  1. 可能原因是线程死锁,可以用jstack 进程id定位到出问题的线程行数和原因,进行修改。

3.本地方法栈:

版权声明:本文为优快云博主「情迷IntelliJ IDEA」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/phg739436146/article/details/108412116

4.堆:

  • 通过new关键字,创建对象都会使用堆内存;
  • 特点:
  1. 它是线程共享的,堆中对象都需要考虑线程安全的问题;
  2. 它有垃圾回收机制;

4.1 堆内存溢出:

  • java.lang.OutOfMemoryError:Java heap space
  • 在判断是否存在堆内存溢出的问题的时候,可以将堆内存相对设置的小一点。

4.2 堆内存诊断:

5.方法区:

  • 定义:

    版权声明:本文为优快云博主「敏叔V587」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.youkuaiyun.com/zhuxuemin1991/article/details/103900190

 5.2:方法区内存溢出:

  • 1.8之前会导致永久代内存溢出:java.lang.OutOfMemoryError:PermGen space;
  • 1.8之后会导致元空间内存溢出:java.lang.OutOfMemoryError:Metaspace;

6.运行时常量池:

  • 二进制字节码:类的基本信息,常量池,类方法定义(包含虚拟机指令);
  • 常量池:就是一张常量表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量(字符串,基本的整数,布尔类型)等信息;
  • 运行时常量池:常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址;

6.2 StringTable(串池):

  • public class StringTable {
        public static void main(String[] args) {
            String s1 = "a";
            String s2 = "b";
            String s3 = "a" + "b";//ab
            String s4 = s1 + s2;//new String("ab")
            String s5 = "ab";
            String s6 = s4.intern();
    
    //问
            System.out.println(s3 == s4);//false
            System.out.println(s3 == s5);//true
            System.out.println(s3 == s6);//true
    
            String x2 = new String("c") + new String("d");//new String("cd")
            String x1 = "cd";//"cd"
            x2.intern();
    
    //问,如果调换了 x1,x2的位置呢?如果是jdk1.6呢?
            System.out.println(x1 == x2);//false
    
        }
    }
  • 特性:
  1. 常量池中的字符串仅是符号,第一次用到时才会变为对象;
  2. 利用串池的机制,来避免重复创建字符串对象。创建对象时是延迟(懒惰)模式;
  3. 字符串变量拼接的原理是StringBuilder(1.8);
  4. 字符串常量拼接的原理是编译期优化;
  5. 可以使用intern()方法,主动将串池中还没有的字符串对象放入串池。

                a.1.8 将这个字符串对象尝试放入串池,如果有则不放入,如果没有则放入,并且会把串池中的对象返回;

                b.1.6 将这个字符串对象尝试放入串池,如果有则不放入,如果没有会把此对象复制一份,放入串池,并且会把串池中的对象返回;

  • StringTable位置:
  1. 1.6在永久代中,1.8在堆内存中;
  • StringTable垃圾回收:
  1. 在内存紧张时才会触发垃圾回收;
  • StringTable性能调优:
  1. 底层是用hash表存储数据,所以分配的内存越大,桶越多,存储的数值越分散,链表短,查找和加入数据效率更高。
  2. 考虑将字符串对象是否入池:如果所提供的字符串中存在大量的重复字符串对象,可以考虑将这些重复的字符串对象使用intern()方法入池,以此来节省内存的占用
  • 直接内存(不属于JVM内存,属于系统内存):
  1. 定义:

        a.常见于NIO操作,用于数据缓冲区;

        b.分配回收成本较高,但读写性能高;

        c.不受JVM内存回收管理(调用一个非常底层的unsafe类的freeMemory()来进行内存释放);

     2.分配和回收原理:

         a.使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法;

         b.ByteBuffer的实现类内部,使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦

            ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调

            用freeMemory来释放直接内存;

7.垃圾回收

  • 如何判断对象可以回收:
  1. 引用计数法(java未采用):存在两个对象相互引用,但并没有第三个对象引用这两个对象的情况。
  2. 可达性分析算法:

        a.java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象;

        b.扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找到,保留;

           找不到,表示可以回收;

        c.哪些对象可以作为GC Root?可以用Memory Analyzer(MAT)工具进行可视化查看。

     3.五种引用:

 //实现表示强引用

        a.强引用: 通常我们使用 new 操作符创建一个对象时所返回的引用即为强引用,只有所有GC Root对象都不通过强引用引用该对象,该对象才能被垃圾回收。

        b.软引用: 若一个对象只能通过软引用到达,且没有GC Root指向,那么这个对象在内存不足时会被回收,可用于图片缓存中,内存不足时系统会自动回收不再使用的Bitmap

        c.弱引用: 若一个对象只能通过弱引用到达,且没有GC Root指向,那么它就会被回收(即使内存充足),同样可用于图片缓存中,这时候只要 Bitmap 不再使用就会被回

//软弱引用在没有引用对象的时候,可以配合被放入引用队列中,从而释放掉引用所占用的内存空间

        d.虚引用:必须配合引用队列使用,主要配合ByteBuffer使用,例如,在其本身所指向的ByteBuffer被回收之后,直接内存并不会被回收,这时需要把虚引用的直接内存地址放入引用队列中,从而调用Unsafe.freeMemory()方法回收掉直接内存的空间。而且虚引用比弱引用还弱的一点是,即使没有垃圾回收,也无法访问虚引用对象。虚引用必须配合引用队列一起使用。
        e.终结器引用: 终结器引用和虚引用的方式很类似,它用以实现对象的finalize()方法。 当一个对象重写了finalize方法,在第一次GC时,对应终结器引用进入引用队列队。由一个优先级很低的Finalizer线程通过终结器引用找到被引用对象并调用它的finalize()方法,第二次GC时才能回收被引用对象。
————————————————
版权声明:本文为优快云博主「zuiziyoudexiao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/zuiziyoudexiao/article/details/89093896
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值