JVM入门学习

1、JVM的位置

 

 

2、JVM的体系结构

 

 

 

 

 

 

3、类加载器

 

 

   1.虚拟机自带的加载器

   2.启动类(根)加载器

   3.扩展类加载器

   4.应用程序(系统类)加载器

 

 

4、双亲委派机制

复制代码
 1 package java.lang;
 2 
 3 public class String {
 4     //双亲委派机制:安全
 5     //1.APP-->EXC-->BOOT(最终执行)
 6     //BOOT
 7     //EXC
 8     //APP
 9 
10     public String toString(){
11         return "Hello";
12     }
13 
14     public static void main(String[] args) {
15         String s =new String();
16         System.out.println(s.getClass().getClassLoader());
17         s.toString();
18 
19         new Thread().start();
20 
21     }
22     /*
23         1.类加载器收到类加载器的请求!
24         2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载 BOOT
25         3.启动类加载器检查是否能够加载当前这个类,能加载就结束,使用当前加载器,否则,抛出异常,通知子类加载器进行加载
26         4.重复步骤3
27         Class Not Found
28 
29         null : java调用不到~C、C++ 或者没有
30         Java = C++--; 去掉繁琐的东西,指针、内存管理
31      */
32 
33 }
双亲委派机制
复制代码

 

 

5、沙箱安全机制

  沙箱是一个限制程序运行的环境

  基本组成

    字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。

    类装载器(class loader):其中类装载器在3个方面对Java沙箱起作用

      它防止恶意代码去干涉善意代码;//双亲委派机制

      它守护了被信任的类库边界;

      它将代码归入保护域,确定了代码可以进行哪些操作。

 

6、Native

复制代码
 1 package com.fang;
 2 
 3 public class TestNative {
 4     public static void main(String[] args) {
 5         new Thread(()->{
 6 
 7         },"My thread name").start();
 8     }
 9 
10     // native : 凡是带了 native 关键字的,说明java的作用范围达不到了,回去调用底层C语言的库!
11     // 会进入本地方法栈
12     // 调用本地方法接口 JNI
13     // JNI 的作用:扩展Java的使用,融合不同的编程语言为Java所用!最初C、C++
14     // Java诞生的时候 C、C++ 横行,想要立足,必须要有调用C、C++的程序
15     // 它在内存区域中专门开辟了一块标记区域:Native Method Stack,登记 native 方法
16     // 在最终执行的时候,加载本地方法库中的方法,通过JNI
17 
18     // 一般驱动硬件时用,Java程序驱动打印机,管理系统,掌握即可,在企业级应用中较为少见
19     private native void start0();
20 
21     //调用其他接口: Scoket通信,WebService,http
22 }
native
复制代码

  具体做法是Native Method Stack中登记native方法,在(Execution Engine)执行引擎执行的时候加载 Native Libraies(本地库)。

 

7、PC寄存器

  程序计数器:Program Couunter Register

    每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

8、方法区

  Method Area方法区

    方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所定义的方法的信息都保存在该区域,此区域属于共享区间;

    //1.6即以前在方法区,1.7以后在堆里

    静态常量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是,实例变量存在堆内存中,和方法区无关。

    static、final、Class模板、运行时的常量池

9、栈

  数据结构

  栈  :后进先出

  队列:先进先出 FIFO

  栈:栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就释放。对于栈来说,不存在垃圾回收问题。一旦线程结束,栈就Over了!

  存放:8大基本类型+引用类型+实例的方法

  栈运行原理:栈帧

 

 

  栈满:StackOverflowError

  栈 + 堆 + 方法区 :交互关系

 

 

10、三种JVM

  Sun公司 HotSpot “Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)”

  BEA “JRockit”

  IBM “J9VM”

 

11、堆

  Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的

  类加载器读取了类文件后,一般会把 类、方法、常量、变量~,保存我们所有引用类型的真实对象

  堆内存还要细分为三个区域:

    新生区(伊甸区) Young/New

    养老区  Old

    永久区  Perm(jdk1.8被元空间取代了)

  GC垃圾回收,主要在新生区和养老区

  假设内存满了,OOM,堆内存不够! java.lang.OutOfMemoryError:Java heap space

  在JDK8以后,永久存储区改了个名字(元空间);

  新生区:

    类:诞生、成长的地方,甚至死亡

    伊甸园区:所有对象都是在这里new出来的!

    幸存者区(0,1)

  老年区

 

   真理:经过研究,99%的对象都是临时对象

  永久区

  这个区域常驻内存的。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境~,这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存~

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

    jdk1.6 之前:永久代,常量池是在方法区

    jdk1.7   :永久代,但慢慢的退化了,“去永久代”,常量池在堆中

    jdk1.8 之后: 无永久代,常量池在元空间

   逻辑上存在,物理上不存在

复制代码
 1 package com.fang;
 2 
 3 public class TestHeap {
 4     public static void main(String[] args) {
 5         //返回虚拟机试图使用的最大内存
 6         long max = Runtime.getRuntime().maxMemory();
 7         //返回JVM的初始化总内存
 8         long total = Runtime.getRuntime().totalMemory();
 9 
10         System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
11         System.out.println("total="+max+"字节\t"+(total/(double)1024/1024)+"MB");
12 
13         //默认情况下:分配的总内存 是电脑内存的1/4,而初始化的内存:1/64
14     }
15 
16     //OOM
17         // 1. 尝试扩大堆内存看结果
18         // 2.分析内存,看一下那个地方出现了问题(专业工具)
19 
20     //-Xms1024m -Xmx1024m -XX:PrintGCDetails
21 }
堆内存初始
复制代码

 

 

 

 

 

 

复制代码
  
package com.fang;

import java.util.Random;

//-Xms8m -Xmx8m -XX:+PrintGCDetails
public class TestOOM {
    public static void main(String[] args) {

        String str = "HappyEveryDay";

        while(true){
            str += str + new Random().nextInt(100000000)+new Random().nextInt(999999999);
        }
    }
}
OOM示例
复制代码

   在一个项目中,突然出现了OOM故障,那么该如何排除~研究为什么会出错

    能够看到代码第几行出错:内存快照分析工具,MAT,Jprofiler

    Dubug,一行行分析代码

  MAT,Jprofiler作用

    分析Dump内存文件,快速定位内存泄漏;

    获得推中的数据

    获得大的对象

    。。。 

复制代码
 1 package com.fang;
 2 
 3 import java.util.ArrayList;
 4 
 5 //Dump
 6 
 7 // -Xms 设置初始化内存分配大小 默认1/64
 8 // -Xmx 设置最大分配内存,默认 1/4
 9 // -XX:PrintGCDetails   //打印GC垃圾回收信息
10 // -XX:HeapDumpOnOutOfMemoryError   //OOM Dump
11 // -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
12 public class TestJprofiles {
13     byte[] array = new byte[1*1024*1024]; //1m
14 
15     public static void main(String[] args) {
16         ArrayList<TestJprofiles> list = new ArrayList<>();
17         int count=0;
18 
19         try{
20             while(true){
21                 list.add(new TestJprofiles());
22                 count += 1;
23             }
24         }catch(Error e){
25             System.out.println("count:"+count);
26             e.printStackTrace();
27         }
28     }
29 }
jprofiles
复制代码

 

12、GC

 

 

   JVM在进行GC时,并不是对这三个区域统一回收,大部分时候,回收都是新生代~

    新生代

    幸存区(from,to)

    老年区

  GC两种类:轻GC(普通的GC)、重GC(全局GC)

 

  GC题目:

    JVM的内存模型和分区~详细到每个区放什么?

    堆里面的分区有哪些?Eden、from、to、老年区、说说他们的特点

    GC的算法有哪些?标记清楚法,标记整理(标记压缩),复制算法,引用计数法,怎么用的

    轻GC和重GC分别在什么时候发生?

  引用计数法

 

 

   复制算法

  幸存区的存在就是为了解决Eden区的空间碎片问题,from和to同理

 

 

     优点:没有内存碎片~

    缺点:浪费了内存空间~:多了一半空间永远是空的。假设对象100%存活(极端情况)OOM

  复制算法最佳使用场景:对象存活度较低的时候;新生区~

  

  标记清除算法

 

 

     优点:不需要额外的空间!

    缺点:两次扫描,严重浪费时间。会产生内存碎片

  标记压缩

    再优化

 

 

13、总结

  内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)

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

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

  

  没有最好的算法,只有最合适的算法---->GC:分代收集算法

 

  年轻代:

    存活率低  复制算法

  老年代:

    区域大、存活率高  标记清楚(内存碎片不是太多)+标记压缩 混合实现

14、JMM

  1、什么是JMM

    Java Memory Model  Java内存模型

  2、作用:

    缓存一致性协议,用于定义数据读写的规则(遵守找到这个规则)。

    JMM定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)

    

 

 解决共享对象可见性这个问题:volatile

posted @   xunzf  阅读(59)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示