前言
不喜欢看长篇大论 所以都是自己(代码界的一个小学生)学习觉得一些可看点 可能有问题 欢迎指出
自己画的一个小图 仅做参考 哈哈哈
.java--------------->.class
.java文件通过编译后生成.class文件 会有类加载器加载到内存中
类加载器
有哪些
1.虚拟机自带加载器
2.启动类(根)加载器
3.扩展类加载器
4.应用程序(系统类)加载器
双亲委派机制
逐级往上委托
应用程序(系统类)加载器->扩展类加载器->启动类(根)加载器->拟机自带加载器
举个栗子:自定义一个java.lang.string,会报错java.lang.String 中找不到 main 方法!
自己定义了一个java.lang.String,会报错,这是因为双亲委派机制,当加载String类时,会逐层往上委托,直到在启动类加载器,而启动类加载器能够加载String类(原来java包下就有),所以加载的是Java中的String,而不是自定义的。
做了啥
1.类的加载:将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后再内存中创建一个java.lang.Class对象用来封装类在方法区内的数据结构。
2.类的连接:
------验证:确保类加载的正确性
a.文件格式验证:保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求;
b.元数据验证:对类的元数据进行语义校验,保证不存在与《Java语言规范》定义相悖的元数据信息;
c.字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的;
d.符号引用验证:确保解析行为能正常执行
------准备:为类的静态变量分配内存,并将其初始化为默认值(比如整型的默认值为0,布尔值的默认值为false);
------解析:把类中的符号引用(通俗的理解就是间接引用)转换为直接引用;
3.类的初始化:为类的静态变量赋予正确的初始值;
继续往下走 就是 运行时数据区了
运行时数据区
程序计数器
一块很小的内存空间,几乎可以忽略不计。也是运行速度最快的存储区域。在jvm规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致多线程从宏观上看是并行的,但实际上是在并发交替执行,cpu在不停的高速切换各个线程,那么切换就需要知道从哪继续执行,JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。(每个线程私有)每个线程在创建后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响。这样就可以保证多线程在并发执行过程中,即使中断,也可以保证分毫不差。
本地方法区
主要存放本地方法中的局部变量,也是每个线程独享的内存区域。(本地方法:该方法的实现由非java语言所实现,比如C,这些方法都是已经实现的)
方法区
用于存放常量,静态变量,类元信息;字节码文件会首先被装载到方法区,这些字节码文件会被堆中的方法所指;所分配的空间从直接内存中获取,不占用jvm的空间;
栈
栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命周期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。
堆
又画一个丑丑的图
这边理解的话 以oom为栗子 我是这么理解的
当你new一个对象,会在伊甸园(Eden Space),然后去幸存0区(Suvivor Space 0),当垃圾回收发现满了,如果对象仍然存活,就会轻gc去幸存1区(Suvivor Space 1),在0区和1区间转(谁空谁是to),多次后,该对象如果还活着,JVM就会认为其为一个持久化对象,则将其移到老年代(Old Gen),老年代满了触发重gc,重gc后如果还是不能创建新对象,那就会报出oom这个异常。
ok以上是我大致的了解和粗鄙的语言概括 哈哈哈
gc的话就放下次写吧
哈哈哈 努力学习的小学生 有错望指出