导图社区 java技术总结
这是一篇关于java技术总结的思维导图,这份思维导图梳理了关于一些工作过程中技术的归类专题的主要知识点,内容覆盖JSR、数据存储、数据格式、通讯协议、后端框架、前端框架、运行环境。
编辑于2021-07-09 15:16:41java 技术总结
技术篇
1 java基础
1 hash
hashmap
1 什么是hashmap?
由hash 散列构成(key,value),通过hash 函数进行 单向函数编码生成 hashcode ,然后放到hashtable 里 ,HashMap 是 HashTable 的轻量级实现,他们都完成了Map 接口,主要区别在于 HashMap 允许 null key 和 null value,由于非线程安全,效率上可能高于 Hashtable。
2 hash 碰撞
对某个元素进行hash函数运算得到的位置 已经被占用
解决方案
1 在hash法 比如首字母运算发现冲突了,在按照首字母第二位继续寻址
2 开发地址法 按照某种函数在继续寻址 知道找到空位置
3 链地址法
创建一个链表数组 数组中每一个格是一个链表 如有冲突 则加到链表中即可 hashmap就是这种解决方案
2 类的加载过程
1 类的加载机制
说明
1 JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用的Java类型的过程
资料地址
https://www.jianshu.com/p/dd39654231e0
步骤
1 加载
将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口
2 链接
说明
将 JAVA类二进制代码合并到JVM的运行状态中
1 验证
确保加载的信息符合JVM虚拟机规范 没有安全风险
2 准备
正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成
3 解析
虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程。
3 初始化
1 初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。
2 当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先初始化其父类。
3 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
2 程序初始化顺序
3 类加载的代理模式
3 java 内存模型
备注
1 在1.5的版本修改过 1,8的沿用
说明
1 调用栈和本地变量存放在线程栈上, 对象存放到堆上
原始类型就是基础类型 int long byte 等
2 一个本地变量可能是原始类型,在这种情况下 它总是呆在线程栈上
3 一个对象可能包含方法 这些方法包含本地变量,那么这些方法依旧存在线程上 即使这些方法的对象存放到堆上
4 一个对象的成员变量存放在堆上,不论变量是不是原始类型
5 静态成员变量 跟类定义一起存在堆上
6 存放在堆上的对象可以被所有持有该对象的线程访问,当一个对象可以访问这个对象的时候也就可以访问其成员变量 如果两个 线程同时调用同一个对象的同一个方法的时候 他们都会访问这个对象的成员变量只是会在每个线程上拥有这个对象的私有拷贝
资料地址
https://zhuanlan.zhihu.com/p/29881777
2 spring
AOP
阐述
1 他是面向对象编程(OOP)的一种补充,用于与业务逻辑无关,但是多个对象有其共同香味和逻辑抽取并封装重复利用的模块 这个模块就是面向切面编程
2 减少系统中的重复代码,降低模块间的耦合度,同时提高系统的可维护性,可用于权限认证,日志 ,事务等处理
实现原理
动态代理
静态代理
说明
1 代理的创建
1 创建代理工厂 代理工厂有三个重要信息
1 拦截器数组
2 目标对象接口数组
3 目标对象
2 在创建代理工厂 默认会在拦截器数组后在增加一个默认拦截器 用于最终调用目标方法
3 当调用 getProxy (获取代理)方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib
2 代理的调用
1 当对代理对象调用 就会触发外层拦截器
2 外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式(外层拦截器 创建内层拦截器 判断是否应该被拦截)
3 当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回
资料地址
https://www.zhihu.com/question/23641679?sort=created
3 springboot
1 spirngboot
1 开始事务及面向切面编程
1 在启动类上增加 @EnableTransactionManagement
2 在方法或类上添加 @Transactional
2 springcloud
1 eureka
实现原理
1 eureka 会默认注册自己
2 eureka 分为 server(注册中心),client 客服端(及可以提供服务,也可以使用服务)
3 client 每隔30秒就像server 发送一次心跳,90秒如果还没有反应就认为这个服务挂掉了,会在60秒内集中销毁挂掉的机器,
4 如果15分钟内百分之85的节点都没有正常心跳 eureka 就认为客户端与注册中心出现了网络问题,不会在接受心跳也不会删除服务
5 集群
1 每一个server(注册中心) 它既是server 业内置了 client
2 也就是说一个server 既可以向其他server端去注册也可以被其他client注册
3 当一个server 以client 身份像其他server注册的时候,也会把自身所携带的client信息一同带入到server中去
4 redis
1 redis 的多种结构
1 string
2 hash
3 set
4 zset
5 list
2 什么场景中用到了 那些结构
1 string
存的字符串
2 hash
相当于 map
3 set
hashset
4 zset
有序集合
5 list
arrayList
3 哨兵模式
5 数据库
锁
悲观锁
多个数据库并发操作会发生冲突,所以总要求加锁操作 悲观锁主要锁表 行锁 页锁
表级锁
读锁锁表,会阻碍其他事务修改表数据。写锁锁表会阻碍其他事务读与写。
是不是说反了 有待考究
页级锁
就是对页进行加锁
行级锁
共享锁(S锁)
又称读锁 如果一个事务对 一条数据上了共享锁,则其他事务只能读,等释放了锁才能对这条数据修改
共享锁可以多个线程同时获取一个锁 一个锁可以被多个线程拥有
排它锁(X锁)
又称写锁,一个事务对数据上了X锁 则 当前事务可以对这条数据读写,但其他事务在当前事务释放锁之前不能对该数据上锁 则其他数据无法读写该数据 直到 当前事务释放
又称独占锁 只能一个线程占用
更新锁
当数据执行 更新的时候 会分配给一个更新锁 当独取信息读取完毕开始更新信息的时候升级称独占锁
更新锁可以跟共享锁同时存在 但是 只允许一个更新锁 ,这样 加入多个事务更新相同数据时候,只有一个更新锁 而变为独占锁 只有独占锁 释放后其他线程才能在加锁 防止了 死锁的存在。
意向锁
在判断每一行是否已经被行锁锁定效率比较低下,因此使用意向锁,当发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
参考资料
https://www.cnblogs.com/panxuejun/p/8874321.html
乐观锁
数据库总认为多个数据库并发操作不会产生冲突,所以总是不加锁操作,所以在数据提交更新的时候才会正式像数据的冲突进行校验 如果用发现侧返回信息给用户 ,让用户去选择 乐观锁实现一般用 版本号和时间戳
在更新的的时候同时更新版本号和时间戳
资料地址
https://www.cnblogs.com/cnndevelop/p/12311071.html
事务
说明
1 是构成单一逻辑的操作数据库动作集合
2 包含一个或多个数据库操作 这些操作构成一个操作整体
3 这些操作要么执行都成功 要么都不执行
4 这些操作对数据库的影响为 要么全部都有影响 要么都不受影响 走之是一致性
5 及时数据库发生故障以及并发事务存在的情况下依然成立
简单举例
BEGIN TRANSACTION //事务开始
SQL1
SQL2
COMMIT/ROLLBACK //事务提交或回滚
一个事务把多个sql包起来 一起处理
特性
原子醒
一致性
隔离性
持久性
隔离级别
读未提交
读已提交
可重复度
串性话
资源地址
https://www.cnblogs.com/takumicx/p/9998844.html
读写分离
说明
一个主库 多从库 主库写入 ,从库读取
实现原理
主库将数据变更写到binlog 日志中,从库链接主库的之后有一个线程io 将主库的binlog 拷贝到本地,写入一个中继日志中,从库中有一个线程会读取中继日志的binlog 然后执行 binlog sql,也就是主库的sql 在本地重新执行一次,保证了数据的一致性,
痛点
1 因为同步是 串行话的 所以在高并发会有延迟延迟性
2 主库突然宕机 从库没来得及把数据同步过来,这样就会草成从库里没有这些数据 也就是说从库里这些数据丢失了
痛点解决
1 半同步复制
解决数据丢失问题
主库写入binlog以后就会强制立即将数据同步到从库。从库会讲日志写到本地的readlog 中接着会同步给主库ack 主库收到ack通知以后证明已经同步完毕
2 并行复制
解决主从同步问题
从库开启多个线程 并行读取 relaylog 不同库的日志 然后并行重方不同库日志 这是库级别的并行
资源地址
https://cloud.tencent.com/developer/article/1373841
6 微服务架构搭建
1 所有的架构首先要考虑业务特点
1 业务成本
2 可扩展性
3 合理的业务拆分
合理的业务拆分主要是是 横向扩展
2 注册中心 可以多台 也可以单台 多台相互注册 防止dow机
3 网关 根据需要 设计 网关 比如 一般分为内部网关 和外部网关 内部网关 判断是用户的登陆信息 等 外部网关是用另一种方式判断
4 可以配合 ngnix 就行负载均衡
5 配置中心
6 链路追踪
7 熔断降级管理
8 发布管理
9 log 查看管理
杂项篇
一些问题
1 你的开源项目
2 项目中发现一个请求10次请求有两次数据不正常,怎么查找问题及解决方案
分析
有可能是一台服务出了问题 1 宕机 导致没有反馈信息,2 其他异常与其他服务器代码不一致等相关原因
处理步骤
2 查出来具体的微服务项目
1 判断是什么架构的项目 该问题 应该考的是 微服务。
3 问题项目进行 降级 或down机处理
4 剩下的是单项目处理
单词
服务器
1 cpu 占用过高怎么查看与解决
解决
1 top 查找占用高的进程号
2 top -p 21723 -H 找到21723 进程号 占用高的线程
3 jstack 21723
jdk 本身提供的堆栈跟踪工具
https://www.cnblogs.com/wuchanming/p/7766994.html
3 多线程
1 多线程的创建方式
2 关键字
1 volatile
1 一个线程上操作数据,对其他线程是立即可见的
2 当变量被volatie 修饰后 编译器和运行时都会注意到这个变量时共享的
3 不会将这个变量上的操作与其他内存操作一起重排
4 变量不会被缓存寄存到其他线程看不到的地方 因此读取时总会返回最新的数据
2 怎么设计线程池数量
线程池
池化技术
1 程序的运行 ,占用系统的资源,优化资源的使用,这是池化技术的本质
2 线程池 链接池 内存池 对象池,创建销毁十分浪费资源
3 在池里准备一些资源 有人来用 给 用完在还给我,这里就省去了 创建和销毁 。
线程池的好处
1 降低资源消耗
2 提高相应速度
3 方便管理
重点 线程服用 可以控制最大并发数,管理线程
线程池运行的三种方式
说明
简单阐述 万变不离其宗,可能springboot以后 会有更简单的 但都一样
1 Executors.newSingleThreadExecutor()
单个线程
使用线程池之后去创建线程,创建 使用完以后就去关闭线程
2 newFixedThreadPool(int nThreads)
创建一个有固定线程的线程池
3 newCachedThreadPool()
缓存池 遇强则强 欲脱则弱
7大参数
1 核心线程池大小
核心线程数会一直存活,及时没有任务需要执行的时候
当线程数小于核心线程数的时候及时有空余线程也会优先创建线程
设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
2 最大核心线程池大小
当任务队列已满时,线程池会拒绝处理任务尔抛出异常
3 超时 时长
当空闲线程超过这个值的时候线程会退出 直到 线程数量等于核心线程数量
如果allowCoreThreadTimeout=true,则会直到线程数量=0
4 超时单位
5 阻塞队列
6 线程工厂
7 拒绝策略
四种拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
并发
1 Java并发控制的最小单位是线程,它是直接由操作系统内核提供的
JVM(深入理解java 虚拟机 第三版本总结)
1.7
1.8
jvm 运行时内存区域展示(虚拟机内存模型)
JVM 虚拟机数据区
线程隔离
程序计数器
说明
1 它可以看做是当前线程所执行的字节码行号指示器
字节码解释器的作用就是通过改变行号就是根据计数器的值来选取下一行需要执行的字节码指令
2 程序控制流的指示器,循环,分之,跳转,异常线程恢复等都需要这个指示器完成
3 因为多线程是通过线程切换轮流执行的,在一个时间都会执行一个线程的指令,因此为了重新切换线程恢复到制定位置 ,所以每个线程都需要有独立的程序计数器,各个线程上的计数器独立运行独立存储互不影响,这类内存区域为线程私有的内存区域
4 每个线程一块,指向当前线程正在字节码代码的行号,如果当前线程执行的是native 方法,则其中为null
暂时理解 navtive 方法 为非当前线程的方法
生命周期
与线程共存亡
本地方法栈
说明
1 与虚拟机栈很相似
2 虚拟机栈是提为了虚拟机执行Java方法提供, 而本地方栈则为虚拟机使用到的native (原生)方法提供
3 对本地方法栈的使用并没有硬性要求,所以 虚拟机可以根据需要自由使用它 hot-spot就直接吧本地方法栈 和虚拟机栈和二为一了
虚拟机栈
当前栈帧
局部变量表
八大原始类型
对象的引用
returnadderss
操作数栈
动态链接
方法出口信息
1 线程私有 ,每个线程对应一个线程虚拟机栈 ,生命周期与线程共进退 每个java 方法在调用的时候都会创建一个栈帧 ,并且入栈,方法结束则出栈,所有的线程都出栈后,线程也就完成了使命
2 通常说的栈 是指虚拟机栈, 或者更过的指向是虚拟机栈中的局部变量表部分
3 局部变量表
八种基础类型
1 这些数据类型的存储空间已 slot 来表示
long 和double 会占用两个槽位
2 对象引用,它可能并不是对象本身,而是被引用对象的引用指针
3 局部变量表空间 是在程序编译期间有已经确定的 因此在程序运行的时候是不会改变的,请注意这里的大小值得是局部变量表的槽位数量
4 由于即时编译技术的进步,逃逸分析技术的日益强大,栈上分配,标量替换,优化手段已经导致了一些微妙的变化,所以Java对象实例都在堆上分配也变的不那么绝对了
生命周期
与线程共存亡
线程共享
堆
1 堆是jvm占用内存最大的,其用途比较单一,就是存放对象实例,几乎所有的对象及数组都在堆上存放
2 java 堆 可以存在于物理上不连续的内存空间,但逻辑上应该被是为是连续的
2 生命周期
1 在虚拟机启动时候创建
2 存放对象实例
几乎所有的Java对象内存都在都在分配
堆空间的内存分配
年老代
三分之二的堆空间
metadata space
年轻代
三分之一的堆空间
eden区
8/10的年轻代空间(也是初始代)
survivor0
1/10
survivor1
1/10
字符串常量池
1.7版本以后 字符串常量池也在对中存放 ,堆有自己进一步的内存分块划分,按照GC 分代划分
方法区
1 运行时常量池
class文件中 用于存放编译其生成的各种字面量与符号引用,这部分内容将在类加载后存放方法区的运行常量池中
本地内存(服务器内存)
元数据区metaspace
1 元数据区 与jdk 1.7永久代类似,都是jvm 规范中方法区的实现
2 元数据区与 永久代最大的区别是 元数据区并不在虚拟机中而是在本地内存
3 永久代和元数据区本质都是方法的实现,方法区存放虚拟机加载的类信息,静态变量,常量等数据
直接内存
服务器内存
常用命令
查看所有jvm默认参数
java -XX:+PrintFlagsFinal -version
关键信息截图
截图说明
-XX:InitialSurvivorRatio
新生代Eden/Survivor空间的初始比例
-XX:Newratio
Old区 和 Yong区 的内存比例
垃圾回收器 与内存分配策略
gc 回收机制
1 新创建的对象都放在 eden区
2 eden 快满了 进行一次清理,不被引用的对象直接干掉,还有引用的对象,但是年龄比较大的挪到s0区 老年代上
3 下一次gc 的时候 会把eden 和S0区放到S1 区 原理上 S0和S1要保持有一个是空的 用来存下一次的对象
4 在下一次 会把endn 和S1年龄比较大的存到S0上,此时S1是空的
5 知道eden 快满了 S0 S1 也快满了,这时候会把这两个区域年龄较大的放到old区上
6 依次循环 知道 eden s0 s1 old 区都快满了 会对整个内存进行依次大清洗 ,腾出内存
了解垃圾回收器的意义
1 需要排查各种内存溢出,与内存泄漏的时候
2 当垃圾回收期成系统并发瓶颈的时候我们必须对自动化的垃圾回收期进行自动化的技术实施 和监控及调节
备注
1 栈上的占用内存的数据会随 线程结束而结束与回收,而且 每个栈帧基本都在类结构确认的时候就已经确定了 所以基本上不会产生内存溢出的问题
2 堆和方法区里边的内存就不确定 比如 一个类可能有多个实现类 每个方法入参不一样也可能导致所需要的内存不一样
垃圾回收算法
1 引用技术式垃圾收集
2 追踪式垃圾收集
1 分代收集
1弱分带假说
1 绝大部分对象都是朝生熄死、
2 因为朝生夕死 所以gc更关心的是如何减少存货量 而不是标记大量将要回收的垃圾
2 强分带假说
1 熬过越多次垃圾回收过程的对象就越难被消灭
2 把他们放在一块虚拟机吧他们放在一块更容易去管理
3 跨代引用假说
1 一个对象即在青年代存在引用 又在老年代存在引用,而因为年轻代引用 无法被回收 所以回晋升到老年代,进而跨代引用消失
2 因为上述 所以没必要每次都扫描老年代 而减轻负担
3 所以在创建对象初期 就吧对象分割 而标注出那些个内存快可能存在跨代引用,从而只对这部分内存加入 GC ROOTS进行扫描 ,虽然多出来这部分的维护 但相对扫描整个年老代来说忍让是划算的
缺点
1 理论上是遍历老年代是可行的 但无疑回对回收带来很大性能负担,
名词
1 部分收集:指不是完成收集整个Java堆的垃圾
1 新生代收集
只 目标只做新生代的垃圾收集
2 老年代收集
只目标只做老年代的垃圾收集
3 混合收集
只目标收集年老代和请年代两个代的收集 目前只有G1收集器会有次行为
2 整堆收集:收集堆和方法区的垃圾
2 标记-清除算法
说明
1 最基础的 清楚算法 原因是后续的垃圾算法几乎都基于这个
2 算法分为标记和清除两部分
标记
首先标记所需要回收的对象
回收
在标记完成后 统计回收被标记的对象
反过来
也可以标记存活的对象 回收没被标记的对象
缺点
1 因为 如果堆中有大量的对象要被标记和回收 则进行大量标记和回收动作效率会随着数据的增大而降低
2 内存空间碎片花的问题,空间太碎片化 会导致 需要分配大内存空间的时候 找不到足够连续的内存空间 不得不提前处罚另一次碎片收集
3 标记 复制算法
说明
1 将内存分为两块,每次只用一块,当一块内存用完了 就把存活的对象复制到另一半上,然后晴空这一办
2 如果存活的数据过多,那么就有较多的复制粘贴的
优点
1 分配内存时 不用考虑使用空间碎片的复杂成都 只需要移动堆顶指针即可
缺点
原油的内存使用旅降低到百分之五十
4 标记-整理算法
说明
1 与标记清除算法一致 ,都是前半部分进行标记,但是 整理算法是 后半部分进行移动,移动到一另一端,清除另一端
缺点
1 移动对象 会时必须暂停用户的使用才能进行
经典的垃圾回收器
说明
经典
目前应在实验状态但是革命效果有高性能改进的所包含的几款收集器
1 Seiral 收集器
1 是一个单线程收集器
2 必须暂停其他其他工作上的线程 才能完成收集
3 所以对用户很不友好 因为会暂停
4 但seiral 它是用额外内存最小的 随着不断的优化 停顿的时间也越来越小了 ,随着微服务的发展 一般一个服务所占用的 内存也会较少 而且不断优化 seriral 也不断优化 收集垃圾的时间可以控制到几十到一百毫秒 只要不频繁收集一般来说客户都是可以接受的
与1.7的区别
1 元数据去取代了永久代
jvm 优化
在 jdk1.7中一般配置
-XX:PermSize=512M -XX:MaxPermSize=1024M
永久代初始值为512 最大为1024,如果超过了1024 就会报 permGen 异常 内存溢出
jdk 1.8以后的一般配置
-XX:MetaspaceSize=512M XX:MaxMetaspaceSize=1024M
如果 meatspacesize 不做配置默认同jinfo 查看获得, 而MaxMetespacesize 则很大很大因为其受服务器内存限制
xms 与xmx 参数设置一致就是为不可扩展
说明
不设置metaspcesieze 则默认21M,如果做了则为自定义大小,空间使用到达fullgc 则扩大该值,如果元空间实际使用小于阙值 ,该值也会缩小
maxmetaSpaceSize 是元空间的最大值 如果设置太小也会OOM
具体参数配置
注意
所有配置都已老年代存活对象进行为基础进行推理
1 jvm 堆大小的设置 xms xmx 设置老年代存活对象的3到4倍,及fullgc 之后老年代内存占用的3.4倍
2 永久代(元空间)设置为老年代存活对象的1.2 到1.5倍
3 年轻代 xmn 设置老年代存活对象的1-1.5倍
4 老年代内存设置老年代存活对象的 2-3倍
资源地址
https://blog.csdn.net/bruce128/article/details/79357870
虚拟机
hotspot VM
热点代码探测能力
通过计数器找到最具有编译能力的代码 ,然后通过即是编译已方法为单位进行编译
方法中有效循环次数多
将会触发即时编译和栈上替换编译
通过编译器和解释器恰当好处的合作,可以在最优化的响应时间和最优化的执行性能取得平衡,不必要等待本地代码输出后在执行程序,即时编译时间压力小,这样有助于引入更复杂的代码优化和,输出质量更高的本地代码
定位
面对不同应用场景全方位的Java 虚拟机
模块化
JDK10
重构了 垃圾收集器接口
栈容量不可以动态扩展
1 虚拟机对象
1 对象的创建
1 首先区检查这个指令的参数是否能在常量池中定义一个类符号的引用,并且检查这个符号引用代表的类是否被加载,解析和初始化,如果没有那么必须做响应的解析,加载初始化,
2 接下来对刚新生对象进行分配内存
1 对象所需要的内存的小 在类加载完后就已经确定了
分配方式
1 指针碰撞
使用场景
1 Java中内存是绝对工整的
分配方式
1 仅仅是指针重空闲的一方挪动一段与对象相等的举例
缺点
1 并不是线程安全的
解决方案
1 同步处理 失败重试原则
2 进行隔离分配,及按线程分配,每个线程提前在对里分配一个空间(本地缓存区),分配新的缓存区才需要同步上锁
2 空闲列表
使用场景
1 当使用的内存和未使用的内存不工整而是互相交错在一起的时候 这时候就不能使用指针了
分配方式
1 虚拟机需要维护一个列表 记录哪块内存跟配给了谁。
备注
1 理论上只能使用较为复杂的 空先列表法
2 为对象分配内存实际上就是把一块确定好大小的内存从Java堆上划分出来
2 对象的内存布局
存储布局划分
1 对象头 header(存储两种数据)
1 存储运行对象本身的数据
1 数据类型
哈希吗
GC分带年龄
锁状态标志
线程持有锁
偏向线程id
偏向时间戳
等
2 数据长度
在32位虚拟机 和64 位虚拟机 分别占用 32 64 个比特
2 对象的类型指针
1 对象指向它类型元数据的指针
注意
1 并不是所有虚拟机都必须在对象数据上保留指针, 及查找对象元数据并不一定非要运用数据指针,换句话 查找对象元数据不一定非要经过对象本身。
2 如果对象是数组 对象头还应该有一块记录数组长度的数据 因为虚拟机是通过元数据给对象分配内存大小 但是数组长度不确定
2 实例数据 instance data
内容
1 程序中定义的字段内容
2 无论是父类 还是子类都必须记录进去,
3 相同宽度的数据会北一分配到一起存放
子主题
3 对齐填充 padding
作用
1 并不是强制性的 而是占位符的作用
2 hotsport 必须是 8个字符的整数倍
3 对象访问定位
两种方式
1 使用句柄
如果使用 句柄 Java 堆里会划出来一个句柄池 句柄池 中存的是对象的实例数据 ,与类型数据各自的具体地址信息

2 直接指针
快 ,省了一次执政定位时间开销,但是但是因为对象的实例访问非常频繁,因此也多了以及几位客观的开发成本,

虚拟机异常
1 堆异常
1 内存泄漏
说明
动态分配的堆内存由于某种原因不能呢释放 或程序未释放 总成系统内存的浪费,导致程序训醒速度慢甚至系统崩溃的严重后果
解决方案
1 通过工具查询泄漏对象到GC Root 的引用链 对象是通过那些应用路径 与gc roots 相连 才导致垃圾回收期无法回收
2 通过1 可以比较准确的定位创建的位置 进而找出内存代码泄漏的具体位置
3 假如不是内存泄漏 检查设置的 -xmx xms 与机器内存进行对比 是否需要向上调整内存
4 代码上检查某些 对象的生命周期 和持有状态时长 存储结构设计是否合理
内存溢出
1 OutOfMemoryError(内存溢出)
内存不足
2 虚拟机栈 和本地方法栈溢出
1 StackOVerflowError(栈溢出)
原因
1 线程请求的深度大于虚拟机允许的最大深度
2 OutOfMemoryError(内存溢出)
原因
1 栈扩充的时候无法申请更过的内存空间
2 HotSportVm 是不会在方法里出现该异常的 因为 hostpot 是不允许扩充的,如果出现该异常 应该是申请栈空间的时候
3 方法区和运行时常量池异常
1.8以上
共同点
不同点
单词
proxy
代理
Seiral
连续
最基础的 最早的垃圾回收器
Collection
metaspace
元空间
存在本地内存上
reference
参考
它用来给普通对像进行包装,从而在JVM在GC时,按照引用类型的不同,在回收时采用不同的逻辑。先来看下这个类的继承体系