浅谈JVM

  • 请谈谈你对jvm的理解?java8虚拟机跟之前的变化更新?
  • 什么事OOM,什么是栈溢出Stack OverflowError ?怎么分析?
  • JVM的常用调优参数有哪些?
  • 内存快照如何抓取,怎么分析Dump文件?
  • 谈谈jvm中,类加载器你的认识?





 

 

 

双亲委派机制:

 

 

 从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

 

 

 

栈:8大基本类型+对象引用+实例的方法

 

8大基本类型:

  • char   对应的包装类 Character
  • boolean   Boolean
  • int  Integer
  • short   Short
  • double   Double
  • byte   Byte
  • float   Float
  • long  Long

 

对象在内存中实例化的过程:

  

复制代码
public class Test5 {
    int age;
    String name;

    public void eat(){

    }

    public static void main(String[] args) throws AWTException {
        int age_a;
        String name_a;
        Test5 test5=new Test5();
        test5.age=1;
        test5.name="hahah";
        test5.eat();
    }
}
复制代码

 

在jvm虚拟机中会有三个区域:堆、栈、方法区

 

代码执行后,类中的成员变量跟成员方法会先进入到方法区中

 

 

当执行到main方法时,main方法会被压入栈中 

 

 

 

当 执行到  Test5 test5=new Test5() 时  会在栈中创建Test5类的引用  然后将成员变量跟成员方法 放入实例中(堆) 都是成员变量跟成员方法的地址值

 

 

 

 

 

 然后会给对象进行赋值   test5.age=1;   test5.name="hahah";  根据地址值在堆中找到 new Test5()对象。

 

方法void() 被执行完毕后 会从栈中弹出(弹栈)  最后main() 方法执行完毕之后也会出栈  (栈遵循先进后出原则)

 

 

堆的理解: 

堆分为:新生区(伊甸区,幸存1区、幸存0区)+养老区+永久区

垃圾回收(GC)一般存在于“伊甸区 ”,称之为 轻GC    “幸存区”是“新生区” 与“养老区” 的过渡区  ,未被轻GC清理的对象存活下来会进入 “养老区” 。当“养老区” 满了后,会进行“重GC”(Full GC)。当堆内存满了之后,会引发OOM(OutOfMermoryError)异常。

在JDK1.8之后 永久存储区改了名字:改为了元空间。

所有的对象都是在伊甸区被new出来的。假设 伊甸区 能存放10个对象 ,当10个对象被new出来后,存满会触发轻GC,GC发现有一个对象还在被引用,另外9个对象属于垃圾,就会将9个对象进行清理,然后一个幸存的对象会进入幸存区。当幸存区也全部

满了之后会进行一次重GC(Full GC) ,再幸存下来的对象就会进入养老区。经过研究,有99%的对象都是临界对象!! 

 

永久区:这个区域常驻内存的。用来存放jdk自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息,

这个区域不存在垃圾回收!关闭jvm虚拟机就会释放这个区域的内存。

一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,知道内存满就会出现OOM

  • jdk1.6之后 :永久代,常量池是在方法区
  • jdk1.7  :永久代,但是慢慢退化了,去永久代,常量池在堆中
  • jdk1.8之后:无永久代,常量池在元空间

元空间:逻辑上存在,物理上不存在

 

复制代码
   public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //返回jvm的总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println(max/1024/1024);
        System.out.println(total/1024/1024);

    }
复制代码

结论:默认情况下,分配的总内存为电脑内存的1/4 初始化的内存是1/64

可使用:-Xms1024m -Xmx1024m -XX:+PrintGCDetails 指令手动修改默认堆空间

 

 

 

 注:-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError   该指令可将OutOfMemoryError 错误生产Dump文件,使用JProfiler工具可对其进行分析从而获取更详细的错误详情。

 

GC算法:

 

  1. 标记清除法

     

     

  2. 标记压缩(优化的标记清除法)
  3. 复制算法

     

     当一个对象经历了15次GC依旧还存活(默认情况下),也可通过 -XX:MaxTenuringThreshold=5 参数来修改默认次数
    由于幸存区的对象在重GC后也会进入养老区,估from区跟to区会互相交换。
    缺点:永远都会多出一块空间。
    优点:没有内存碎片。

  4. 引用计数器  :每个对象被引用一次之后会被加1,但是这个过程会消耗资源

     

     

总结 :

  内存效率:复制算法>标记清除算法>标记压缩算法

  内存整齐度:复制算法=标记压缩算法>标记清除算法

  内存利用率:标记压缩算法=标记清除算法>复制算法

GC中的分代收集算法就是在不同的区使用不同的算法,算是GC的最优算法

 

posted @   码农小白David  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示