目录

ThasBlog

学无止境

标签: Java (16)

记一次线上 JAVA 程序 OOM 事件 有更新!

依赖三方包: <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- 阿里云SLS提供的SDK --> <dependency> <groupId>com.aliyun.openservices</groupId> <artifactId>aliyun-log</artifactId> <version>0.6.56</version> </dependency> 代码: public class DefaultServiceClient2 extends DefaultServiceClient { // 问题的主要原因出在这个 DefaultServiceCl....

Jdk 自带工具

jps, jstat, jinfo, jstack, jmap, jconsole, visualVm jps java 进程状况查看 jps -q 只显示进程号 jps -m 查看启动类 jps -v 查看详细启动参数 jstat jstat -class 显示类加载情况 jstat -gc [统计间隔] [统计次数] 查看 gc情况 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT CGC CGCT GCT 44480.0 44480.0 0.0 8352.5 355840.0 352724.5 889256.0 296943.0 522908.0 503674.2 67120.0 59766.6 6169 27.923 6 6.963 50 6.401 41.287 C: capacity U: used S0: suvivor0 S1: suvivor1 E: eden O: old M: metaspace CCS: 压缩类 C: count T: time 时间 YG: 年轻代GC FG: fullg....

Java 方法调用的底层实现 有更新!

Java 方法调用通过字节码指令 invokestatic, invokespecial, invokevirtual, invokeinterface, invokedynamic 来实现方法调用. 非虚方法 非虚方法指能够在运行时直接确定其调用地址的方法, 这些方法在类加载完成之后, 其内存地址都是不会再变化的, 可以直接访问调用. invokestatic: 静态方法调用 invokespecial: 私有实例方法调用, 构造方法或 super 方法, 被 final 修饰的实例方法 虚方法 由于多态的特性, 方法可以被不同的子类覆写, 所以对于同一个方法, 它们的实现不同, 对应的方法的目标地址也是不一样的, 通过查方法表来实现动态调用. invokevirtual: 可被覆写的实例方法, 大部分调用方法都是这种方式. Java 对象的方法表存放在类中(与 C++不相同), 从对象实际类型开始递归向上查找类型的方法表, 第一次执行过后, 以方法位置作为索引编号建立缓存 invokeinterface: 类似invokevirtual, 但比较慢, 因为接口中方法的位置编号是无法....

JAVA 对象头 有更新!

对象头结构 偏向锁与 hash 不能共存, 一旦计算过 hash, 对象就不可偏向。 对象头中的 bl:1 代表是否可偏向, 若不可偏向, 则前 56 位可以用来表示 hash 值, 若可偏向, 则前 56 位必不表示 hash 值, 可以用来表示偏向的线程 ID Object 没有属性, 所以除了对象头就没有其他内容了, 根据 8 字节对齐, 自动填充了 4 字节 Object[]数组会额外使用 4 字节保存长度, 所以数组的最大长度为整形; 后面则是顺序存储的全部元素的指针, 由于开启了指针压缩, 每个都是 4 个字节。 指针压缩 32 位 CPU 的最大寻址空间为 2^32, 如果这些寻址空间指向 bit 位, 那么最大支持的内存为 512M; 但是 CPU 存储数据的时候都是以 8bit 为一组, 也就是 1byte; 把寻址空间指向 byte 组, 就可以让最大支持内存增加到 4G. JVM 指针压缩原理相同。 64 位 JVM 最大寻址空间是 2^64, 寻址空间指向 byte 组, 则最大可支持 2^34G 内存; 但是存储 64 位指针会让内存占用提高。 由于 J....

Java AQS 有更新!

AQS AbstractQueuedSynchronizer, 队列同步器, 支持队列等待。 继承自 AbstractOwnableSynchronizer, 不带队列的同步器, 只能有一个线程同步, 其他线程忽略。 AQS 使用 state 和 一个 FIFO 队列来进行线程同步。 state 表示申请资源的状态, 默认 0, 若已被占用则小于 0. AQS 不关注申请和释放资源的方式, 只提供了操作 state 的方法。 在并发申请资源过程, AQS 维护各个线程获取资源的原则, 如不满足获取资源的条件, 则先将它们放入等待队列, 等待资源可以被申请了, 再唤醒等待队列中的线程。 申请和释放资源的方法需要锁自己实现: 1// 尝试申请资源 2boolean tryAcquire(int arg) 3// 尝试释放资源 4boolean tryRelease(int arg) 5// 资源是否被独占 6boolean isHeldExclusively() 7// 尝试以共享模式申请资源 8int tryAcquireShared(int arg) 9// 尝试以共....

Java JMM 有更新!

JVM 使用线程工作内存和主内存这两个概念来抽象物理内存结构, 从而屏蔽掉各平台各操作系统的内存结构差异。 JMM 为保障这套抽象内存结构的并发安全, 提供了协议和工具的支持。

Java8 ConcurrentHashMap 有更新!

ConcurrentHashMap 是 HashMap 的线程安全版本, 使用了 CAS, volatile 和 synchronize 技术。 初始化时使用 volatile 修饰的 sizeCtl 作为互斥条件, 如果有其他线程正在操作则 Thread.yeild(). 在插入元素时, 如果元素不存在, 则使用 CAS, 在插入新元素时, 如果元素不存在才能插入成功, 否则失败重试, 如果是 MOVED 状态, 则先辅助扩容, 完成后再尝试插入。 如果元素存在, 需要进行修改或者 Hash 冲突, 则加 synchronize 锁进行处理(因为情况比较复杂, 可能是链表, 可能还需要树化), synchronize 修饰 bin 上的第一个元素 元素数量修改时, 并发高的情况容易 CAS 失败, 引入 counterCells 数组减小 CAS 失败的概率 数组扩容时, 分段迁移, 每段最少 16, 迁移前 CAS 设置 transferIndex, 设置成功则得到该小段的迁移的权利。 迁移时, 如果该 bin 上有值, 则 synchronize 锁住, 如果无值, 则 CAS....

Java8 HashMap 有更新!

HashMap Map 意思是映射, 从 key 映射到 value. HashMap 就是基于 hash 的 key 来实现映射关系。 数组是在连续的固定大小的内存空间上顺序且紧凑地存储元素的集合。 数组支持随机读写, 其本质原因就是紧凑存储。 通过数组头部位置 + 索引就可以直接获得指定元素的地址, 对该地址直接进行读写。 但紧凑存储同时带来另一弊端, 为了保证数组元素紧凑, 每次在数组中间增删元素时都需要挪动数组元素以保证数组中间没有空缺。 数组索引 = 元素在数组中的位置, 数组索引是顺序的且永远不会重复。 Hash 表继承了数组的优点, 支持随机读写, 又不需要(频繁)挪动元素。 牺牲了少量额外内存空间和散列计算开销。 Hash 索引 = 散列函数(元素), Hash 索引不是顺序的, 只与元素本身相关, 但是是可能重复的。 当 Hash 索引重复时, 一个索引会对应多个元素, 这就是 Hash 碰撞, 这时 Hash 表随机读写的优势就会降低。 适当的增加 Hash 表分配的内存空间可以减少 Hash 碰撞几率, 所以 Hash 表通常会有额外的未利用内存空间。 链地址法(....

Jvm GC 示例代码 有更新!

jvm参数: -Xms1000m -Xmx1000m -XX:NewRatio=9 -XX:SurvivorRatio=8 -XX:-UseParNewGC -XX:-UseConcMarkSweepGC -XX:+UseSerialGC -XX:+PrintGC -XX:+PrintGCDetails -XX:-UseTLAB java代码: public class Main { static Object[] bs = new Object[500]; public static void main(String[] args) throws Exception { //预留时间连接console Thread.sleep(20000); // 消除程序初始化的影响 System.gc(); for (int i = 0; i < bs.length; i++) { // 分配1mb //建立强引用 bs[i] =new byte[1024*1024]; Thread.sleep(200); } System.gc(); Thread.sleep(10000); } } ....

JVM GC 有更新!

对象引用 强引用 软引用 OOM 之前回收 弱引用 GC 扫描到就回收 幻象引用 不可使用 对象存活分析 引用计数 可达性分析 GCRoots: 栈上引用 存货的线程 方法区静态变量引用 方法区常量引用 本地方法栈引用 对象晋级原则 MinorGC Eden 区进入 Suvivor 区 Suvivor 区对象年龄 +1 Suvivor 对象年龄达到 15(MaxTenuringThreshold 可以设置, CMS 为 4), 晋级到老年代 Suvivor 对象年龄从小到大计算总和, 到达 50%, 超过的年龄将作为新的年龄上限 大对象无法在新生代分配, 直接分配在老年代 MajorGC 只有 CMS 可以独立执行, 其他回收器都会伴随 MinorGC. 垃圾回收算法 复制 标记清除 标记整理 经典垃圾回收器 Serial/Serial Old 初代 GC, 单线程模式, 必须要 Stop The World SerialOld 是最经典的老年代 GC, 可以与任意的新生代 GC 一同使用 ParNew/ConcurrentMa....

JVM类加载过程

类加载过程包括加载, 验证, 准备, 解析, 初始化.

JVM对象创建与访问 有更新!

对象创建 创建过程 检查加载 检查符号引用, 如未解析, 则类加载 分配内存 内存空间初始化 赋初值 对象头设置 初始化 划分内存方式 指针碰撞(内存整理) 空闲列表(标记清理) 并发安全 TLAB(线程本地缓冲) CAS抢占 对象内存布局 对象访问定位 句柄 间接引用堆上的实例(Hotspot使用) 安全, 方便GC 直接引用 对象分配策略 栈上分配 经过逃逸分析, 无法逃逸的对象可能在栈上直接分配 标量替换, 如果无法逃逸的对象只有标量属性赋值和访问操作, 则不创建对象, 直接拆分成栈上标量 TLAB线程本地分配 正常堆上分配 优先Eden区 大对象直接分配在老年代 分配空间担保原则 新生代垃圾进入老年代前的担保 默认: 当老年代可用空间大于新生代总对象大小, 则可分配; 如不满足, 进入担保策略, 老年代可用空间大于历史进入老年代的垃圾平均值, 则可分配, 如果分配失败, 则FullGC; 如不满足, 直接FullGC 关闭HandlePromotionFailure担保: 当老年代可用空间大于新生代总对象大小, 则可分配; 如不满足....

JAVA 内存区域 有更新!

Java 内存区域包括虚拟机栈, 本地方法栈, 程序计数器, 堆, 方法区。

Spring的BeanFactory 有更新!

Spring BeanFactory = 简单工厂模式 + 策略模式 + Scope + 定制单例容器 +其他增强功能

如何解释spring是什么 有更新!

spring 一般特指 spring-framework. spring 是一个便捷的开发快捷框架,它为企业和个人应用开发提供了基础的框架支持。

Java, Java Bean 和 Spring Bean 的区别

Java是编程语言, Bean是脱离于编程语言的具有特定特征的对象 , Java Bean是基于Java语言实现的用于表示Bean的规范.