JVM-运行时数据区域(一)
1、运行时数据区域Java虚拟机所管理的内存主要包括以下几个运行时数据区域:
1.1、程序计数器 程序计数器是一块较小的内存空间,他可以看作是当前线程所执行的字节码的行号指示器。
在Java虚拟机的概念模型里,字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、
跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
程序计数器-线程私有
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的本地(Native)方法,这个计数器值则应为空(Undefined)
程序计数器是jvm内存区域中唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError的区域
1.2、Java虚拟机栈 线程私有,它的生命周期与线程相同。
虚拟机栈描述的是Java方法执行的线程的内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、
...
NoClassDefFoundError和ClassNotFoundException
1、前言在写Java程序的时候,当一个类找不到的时候,JVM有时候会抛出 ClassNotFoundException 异常,而有时候又会抛出 NoClassDefFoundError。看两个异常的字面意思,好像都是类找不到,但是JVM为什么要用两个异常去区分类找不到的情况呢?这个两个异常有什么不同的地方呢?
2、ClassNotFoundExceptionClassNotFoundException 是一个运行时异常。从类继承层次上来看,ClassNotFoundException 是从 Exception 继承的,所以 ClassNotFoundException 是一个检查异常。
当应用程序运行的过程中尝试使用类加载器去加载Class文件的时候,如果没有在 classpath 中查找到指定的类,就会抛出 ClassNotFoundException 。一般情况下,当我们使用Class.forName()或ClassLoader.loadClass()或ClassLoader.findSystemClass()在运行时加载类的时候,如果类没有被找到,那么就会导致JVM抛出 Clas ...
Exception和Error
1、比Exception和Error,另外,运行时异常与一般异常有什么区别?Exception和Error都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
Exception和Error体现了Java平台设计者对不同异常情况的分类。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
Exception又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的Error,是Throwable不是Exception。
不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayI ...
JVM配置参数
机器配置:8核16G
1、启动参数VM Arguments: jvm_args: -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.io.tmpdir=/tmp -Djava.net.preferIPv6Addresses=false -Xss512k -Xmx12g -Xms12g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+AlwaysPreTouch -XX:ReservedCodeCacheSize=240m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:InitiatingHeapOccupancyPercent=40 -XX:MaxGCPauseMillis=100 -XX:+TieredCompilation -XX:CICompilerCount=4 -XX:-UseBiasedLocking -XX:+PrintGCDetails -XX:+Prin ...
Redis数据结构-Hash
1、Redis的hash结构简介Redis使用**一个全局Hash表来保存所有的键值对具体值的指针(*key, *value 不是数据本身)**,从而既满足应用存取Hash结构数据需求,又能提供快速查询功能。
一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。所以,我们常说,一个哈希表是由多个哈希桶组成的,每个哈希桶中保存了键值对数据。
2、hash冲突
Redis会对哈希表做rehash操作。rehash也就是增加现有的哈希桶数量,让逐渐增多的entry元素能在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突。那具体怎么做呢?
其实,为了使rehash操作更高效,Redis默认使用了两个全局哈希表:哈希表1和哈希表2。一开始,当你刚插入数据时,默认使用哈希表1,此时的哈希表2并没有被分配空间。随着数据逐步增多,Redis开始执行rehash,这个过程分为三步:
给哈希表2分配更大的空间,例如是当前哈希表1大小的两倍;
把哈希表1中的数据重新映射并拷贝到哈希表2中;
释放哈希表1的空间。
这个过程看似简单,但是第二步涉及大量的数据拷贝,如果一次性把哈 ...
Reactor模型
单线程Reactor模式一个线程:单线程:建立连接(Acceptor)、监听accept、read、write事件(Reactor)、处理事件(Handler)都只用一个单线程。
多线程Reactor模式一个线程 + 一个线程池:单线程:建立连接(Acceptor)和 监听accept、read、write事件(Reactor),复用一个线程。工作线程池:处理事件(Handler),由一个工作线程池来执行业务逻辑,包括数据就绪后,用户态的数据读写。
主从Reactor模式三个线程池:主线程池:建立连接(Acceptor),并且将accept事件注册到从线程池。从线程池:监听accept、read、write事件(Reactor),包括等待数据就绪时,内核态的数据I读写。工作线程池:处理事件(Handler),由一个工作线程池来执行业务逻辑,包括数据就绪后,用户态的数据读写
具体的可以参考并发大神 doug lea 关于Reactor的文章。 http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf再提一点,使用了多路复用,不一定是使用了Reacto模型 ...
RadixTree优劣
RadixTree优势
本质上是前缀树,所以存储有「公共前缀」的数据时,比 B+ 树、跳表节省内存
没有公共前缀的数据项,压缩存储,value 用 listpack 存储,也可以节省内存
查询复杂度是 O(K),只与「目标长度」有关,与总数据量无关
这种数据结构也经常用在搜索引擎提示、文字自动补全等场景
RadixTree不足
如果数据集公共前缀较少,会导致内存占用多
增删节点需要处理其它节点的「分裂、合并」,跳表只需调整前后指针即可
B+ 树、跳表范围查询友好,直接遍历链表即可,Radix Tree 需遍历树结构
实现难度高比 B+ 树、跳表复杂
Redis中 Stream 在存消息时,推荐使用默认自动生成的「时间戳+序号」作为消息 ID,不建议自己指定消息 ID,这样才能发挥 Radix Tree 公共前缀的优势。
每种数据结构都是在面对不同问题场景下,才被设计出来的,结合各自场景中的数据特点,使用优势最大的数据结构才是正解。
强引用、软引用、弱引用、幻象引用有什么区别?
1、说明不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。
强引用(“Strong” Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,就是可以被垃圾收集的了,当然具体回收时机还是要看垃圾收集策略。
软引用(SoftReference),是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当JVM认为内存不足时,才会去试图回收软引用指向的对象。JVM会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
弱引用(WeakReference)并不能使对象豁免垃圾收集,仅仅是提供一种访问在弱引用状态下对象的途径。这就可以用来构建一种没有特定约束的关系,比如,维护一种非强制性的映射关系,如果试图获取时对象还 ...
Java对象创建XMind
Java对象的创建
创建对象内存分配方式
空闲列表
不规整空间
CMS
指针碰撞
连续规整空间
Serial ParNew
简单高效
带压缩算法的GC收集器使用
CMS分配方式
Linear Allocation Buffer 空闲列表拿到一大块分配缓冲区之后,在它里面仍然可以使用指 针碰撞方式来分配
并发安全问题
分配动作通过CAS + 失败重试
预分配 本地线程分配缓冲(TLAB), 不够在使用CAS
-XX:+/-UseTLAB
对象的内存布局对象头
存储对象自身的运行时数据
哈希吗
GC分代年龄
锁状态
线程持有的锁
偏向线程ID
偏向时间戳
类型指针(对象指向它的类型元数据的指针)–不是必须的
作用:确定对象是那个类的实例
实例数据
各种类型的字段内容(包含从父类中继承的)
存储顺序会 受到虚拟机分配策略参数(-XX:FieldsAllocationStyle参数)和字段在Java源码中定义顺序的影响
HotSpot虚拟机默认的分配顺序为longs/doubles、ints、shorts/chars、by ...