JVM概述和实战-0

精通Java虚拟机必读书籍推荐书单

走进Java

1996.1.23,JDK1.0发布

1998年Java 1.2版本发布,1999年发布Java 1.2的标准版,企业版,微型版三个版本,为了区分这三个版本,分别叫做Java2SE,Java2EE,Java2ME,简称J2SE,J2EE,J2ME。故,2的含义为1.2版本。java1.2是java中很重要的一站。之后的1.3,1.4与1.2相比,有一些变化,但不是特别大。所以在1.5推出以前一直在使用j2ee这个叫法,没有叫做j3ee,j4ee。2005年。java推出1.5版本,这又是java发展史上的一个里程碑,与之前的java版本有了较大的变化,所以sun决定不再使用这个j2ee这样的称呼,当然也没有使用j5ee。J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME。所以,现在的J2EE等叫法是05年以前老一辈的叫法。

内存区域

在这里插入图片描述

程序计数器

一块较小的内存空间,可以看作当前线程执行字节码的行号指示器。
字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令。

由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式实现的,再在任何一个确定的时刻,一个处理器(对于多核来说是一个内核)都只会执行一条线程中的指令,因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

执行Native方法时,计数器值为空(Undefined),此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈

线程私有,生命周期与线程相通。
虚拟机栈描述的是Java方法执行的线程内存模型,Java方法执行时创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表所需的内存空间在编译期间完成分配

本地方法栈

与虚拟机栈作用类似,但是为Native方法服务

Java堆

虚拟机管理的内存中最大的一块,被所有线程共享。
虚拟机启动时创建,存放对象实例。
主流的Java虚拟机都是可扩展的,(通过参数-Xmx 和-Xms设定)
Java堆是垃圾收集器管理的内存区域,因此一些资料中也被称为“GC堆”。

方法区

各个线程共享的内存区域,用于存储被虚拟机加载的类型信息、常量、静态常量、即时编译器编译后的代码缓存等数据。

运行时常量池

运行时常量池是方法区的一部分。Class文件中除了类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

  1. Java虚拟机对Class文件每一部分(包括常量池)的格式都有严格规定。
  2. 运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,运行时也可将新的常量放入池中,如String.intern()方法。

直接内存

NIO(New input/output)是JDK1.4中新加入的类,引入了一种基于通道(channel)和缓冲区(buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过堆上的DirectByteBuffer对象对这块内存进行引用和操作。这样在一些场景中提高性能,因为避免了在Java堆和Native堆中来回复制数据。

直接内存的大小并不受到java堆大小的限制,甚至不受到JVM进程内存大小的限制。它只受限于本机总内存(RAM及SWAP区或者分页文件)大小以及处理器寻址空间的限制(最常见的就是32位/64位CPU的最大寻址空间限制不同)。

HotSpot虚拟机对象

对象的创建

  1. 类加载检查
  2. 分配内存
    指针碰撞:Java堆中内存是绝对规整的,使用过的内存和空闲内存被一个指针作为分界点隔开。

JVM性能调优

sychronized(this)方法加锁,不能解决多进程并发(负载均衡)

在这里插入图片描述

Jmeter:模拟压测

分布式锁

Redis实现

setnx key value 【set if not exists】
基本实现
基础
2. 异常处理
异常处理
3. 超时处理,防止异常导致锁没有释放,锁变成死锁【异常:如网络抖动,redis服务器宕机】

在这里插入图片描述
设超时之前系统可能挂了
在这里插入图片描述
所以可做如下改进:
在这里插入图片描述
但是,如果业务代码执行时间超过10秒,会导致锁失效,自己加的锁可能被别人删掉 ???

在这里插入图片描述
可改进:每个人只能删自己的key
在这里插入图片描述
超时问题仍未解决,如果if 后卡顿,就会把别人的锁删掉,所以finally要保证原子性
在这里插入图片描述
可以用看门狗机制,线程判断是否执行完
框架简单实现

redisson框架实现分布式锁

【单机,集群,主从,哨兵】
redis常见结构的命令需要熟悉!!! String Hash Set List 参考redis命令手册
实现:

在这里插入图片描述

源码分析
lua脚本具有原子性,这是分布式锁的关键
在这里插入图片描述
future执行完后执行listener
在这里插入图片描述
锁续命,还是使用lua脚本保证原子性
在这里插入图片描述

redis主从结构,又有问题,如果主redis突然挂了,锁丢失和锁失效

在这里插入图片描述
可以使用zookeeper,解决主从问题
redis架构:尽快可用,主节点加成功立即返回成功
zookeeper:主节点会等其他节点添加成功(半数以上添加成功)才 返回成功【zookeeper底层原理需要了解
redis性能比zookeeper强,
权衡:性能(用redis)与强一致性(zookeeper)
既要redis性,又要一致性
Redlock:超过半数redis节点添加成功,才算成功,和zookeeper类似,不推荐用

分布式锁,将并行变为串行,降低了效率,如果解决效率问题?
高并发分布式锁

分段式锁,200个库存,分为10段,每段20个库存
负载均衡,轮询。。。。如何实现?

缓存与数据库双写不一致

在这里插入图片描述

在这里插入图片描述
延迟双删,线程2 sleep,但是影响效率,很少用
队列,也不合适
在这里插入图片描述
目的:写数据库和删除缓存是原子性,就能解决缓存问题,

  1. 通过上述分布式锁实现,但是带来了性能问题

  2. 读写锁
    大部分场景是:读多写少
    读写锁:读和写互斥

  3. 真实场景
    大部分互联网公司,就是在更新缓存时加个超时时间,这样效率更高
    因为很多公司,缓存和数据库不要求严格一致,因为真正下单时是以实际库存为准而不是显示库存

  4. 读写都多,又要求绝对一致(如:转账),一般不会用缓存,直接操作数据库
    乐观锁:性能还可以,但是并发时,大量写操作会失败,不太好

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值