初级程序员应掌握的核心:内存管理、类加载器、对象的创建过程
中高级程序员应该掌握性能调优(JVM调优指的就是对堆内存的优化!)
冯诺依曼机结构
知识体系很重要,囫囵吞枣学习再查漏补缺,要先纠结树干,而不是纠结于树叶!!
内存管理
其实内存管理是由OS去管理的,分配内存空间供进程使用,有两种管理方式Heap和Stack,对于堆区来说地址空间是不连续的,由程序员去分配和释放,如果没有被程序释放那么程序结束自动由OS去回收,换句话说就是Heap中数据未被释放那么其生命周期等同于程序的生命周期,Heap区由程序员使用malloc()函数向操作系统申请内存,使用完可使用free()释放这块内存,这就意味着JVM这个程序可以管理操作系统分配给其的内存空间,也就意味着虚拟机可以对自己管理的内存空间进行垃圾回收,但有可能出现内存泄漏(就是内存浪费明明不适用了还占用着空间!)。Stack是由操作系统分配释放,无需人工控制,存放局部变量和函数参数,数据随着函数的执行完成而结束,每个线程都会有一个Stack,Stack中包含Stack frame(栈帧),代表一个函数。 Heap和Stack最大的区别就是Stack不需要手动释放!堆可能会产生管理不当而内存泄漏!
Java Memory Model
有一个说法有问题,运行时数据区这五块可以叫Java内存结构,Java内存模型是另一个东西,这俩差别挺大的
内存区域是指 Jvm 运行时将数据分区域存储,强调对内存空间的划分。而内存模型(Java Memory Model,简称 JMM )是定义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存中的工作方式
Runtime Data Area
注释:永久代/永久区,新生代/新生区,老年代/老年区
Stack
主管程序的运行 生命周期和线程同步 线程结束就是栈内存释放 对于栈来说不存在垃圾回收问题
每个线程对应一个独立的栈 ,程序计数器记录线程的,线程停止了再继续就需要程序计数器来恢复线程的信息从而继续运行线程
Heap
一个JVM只有一个堆内存空间 堆内存大小可以调节的 堆也是 Java 内存管理的核心区域。
《Java 虚拟机规范》中对 Java 堆的描述是:
所有的对象实例都应当在运行时分配在堆上,方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除
什么是新生代?
类诞生和成长的地方 甚至销毁的过程
伊甸园 所有的对象都是在伊甸园区 new出来的
幸存者区(0,1)
Method Area
方法区的内容属于一种规范!!!
方法区被所有线程共享 此区域属于共享区间
静态变量(static) 常量(final) 加载的类信息(Class) 常量池 存在方法区中
这个区域是常驻内存的 用来存放JDK自带的Class对象 interface元数据 存储的是java运行时的一些环境或类信息 这个区域不存在垃圾回收 关闭JVM虚拟就会释放这个区域的内存
方法区是所有线程共享的内存,在java8以前是放在JVM内存中的,由永久代实现,受JVM内存大小参数的限制,在java8中移除了永久代的内容,方法区由元空间(Meta Space)实现,并直接放到了本地内存中,不受JVM参数的限制(当然,如果物理内存被占满了,方法区也会报OOM),并且将原来放在方法区的字符串常量池和静态变量都转移到了Java堆中
元空间的物理地址就是在本地内存中,因为类的信息大小难以确认导致永久代大小不好确定,类信息太大的时候容易导致内存溢出,所以才把它从jvm移出来,其实他可以理解成永久代的完全改造体,它变得纯粹了,它只用来存放类信息,而不会向之前一样保存一些杂项,而运行时常量池也是放在方法区中的,因为每个类加载时都有对应的常量池表,它是类加载的重要信息
Native Stack
Garbage Collection
what is garbage?
没有任何引用指向的堆内存空间
how to find a garbage?
引用计数法
可达性算法
how to remove a garbage?
Mark-Sweep、Copying、Mark-Compact
内存效率:复制算法>标记清除>标记压缩算法
内存整齐度:复制算法=标记压缩算法>标记算法
内存利用率:标记压缩算法=标记清除算法>复制算法
garbage collectors
堆内存逻辑分区
what is OOM?
- 一个启动类 加载了大量第三方jar包 Tomcat部署太多的应用 大量动态生成的反射类 不断地被加载 直到内存满 就会出现out of memory
how to resolve it
- 能够看到第几行出错
- Debug 一行一行分析代码 生产环境根本没时间一行一行分析!!
- 使用Jprofile内存诊断工具分析Dump内存文件 快速定位内存泄漏
- 获得大的对象
Class Loader
当你运行一个Java程序的主函数时,JVM(Java虚拟机)才会启动,通过Bootstrap ClassLoader加载这些核心类库,以确保java程序正确运行,类加载是一种惰性的过程,当你使用这个类的时候才会进行类加载,将.class文件加载到内存中(方法区),类加载器就是将这些静态数据结构转换成方法区运行时的数据结构,然后在堆中形成代表这个类的java.lang.Class对象 作为方法区中类数据的访问入口
类加载器有哪些类
BootStrap启动类加载器 (负载加载Java核心类库)
Extension扩展类加载器(负载加载Java扩展类库)
Application应用程序加载器(加载应用程序的类)
自定义加载器
堆内存中的Class对象有什么用?
常用于反射机制,Java反射机制指的是在java程序运行的过程中动态的获取类的信息包括类属性、类方法等类信息
类加载器会加载基元类型吗
不会 引用数据类型例如String Integer HashMap 数组等引用类型
基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载。
什么是双亲委派机制?
ClassLoader收到加载请求 首先委托给父类加载 只有当父类不可加载的时候 子加载器才会加载
请求是从下往上委派 加载是从上往下
双亲委派机制有什么好处?
确保该类全局唯一性!当程序出现多个同名的类类加载器在执行加载时只加载一个类 会抛出异常
八股文
JDK1.8运行时内存区域发生了哪些变化?
对象的创建过程?
内存分配会有线程安全问题,CAS、TLAB
对象在内存中的布局?
- markword 8字节(64位 和64CPU保持一致!)
- class pointer 4字节 类型指针 指向实例化所使用的类
- instance data 实例数据 成员变量
- padding 对齐 保证数据总长度被8字节整除(JDK8 HotSpot实现的64位虚拟机的未开启压缩的情况)
new Object()占多少个字节(不存在属性!!)?
markword 8 字节+Class pointer 4字节 + 方法'不占'字节+padding 4字节(补齐8字节的倍数) 16字节