Administrator
发布于 2023-04-10 / 12 阅读
0
0

随笔-1

TCP/IP协议

三次握手——1.客户端发送SYN包给服务端 2.服务端返回SYN/ACK 3.客户端发送ACK

超时重试——发送者一定时间内没收到接受者回信,视为本次包丢失,需要重新补发。

滑动窗口——发送端一次发送包的大小。

JVM内存区域

  • 程序计数器,线程私有,主要是流程控制和上下文切换记录状态,唯一不会出现oom的区域。
  • 虚拟机栈,线程私有,由一个个栈帧组成,每个方法调用时产生,每个栈帧由局部变量表,操作数栈,动态链接,返回地址等组成,方法的运行就是入栈和出栈的过程。
  • 本地方法栈,线程私有,它和虚拟机栈很类似,支持对本地方法的调用,同样也是每个线程创建一个。
  • 堆,JVM中内存划分最大的区域,主要存放对象,是GC重点管理的区域,也称GC堆。可细分为新生代,老年代的,有各种分代回收算法的GC。
  • 方法区,主要存放已经被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。jdk1.8后实现方式由永久代变成了元空间。
  • 运行时常量池,主要存放编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)的 常量池表(Constant Pool Table) ,属于方法区的一部分。

volatile

首先了解下java的内存模型(JMM),JMM 规定所有的变量都需要存放在主内存中,同时,每个线程又有着自己的工作内存 (主要做高速缓存)。线程工作需要操作变量时,会将主内存中的数据拷贝到工作内存中,对数据如果做了更新,还需要将更新后的数据刷新到主内存中。工作内存与主内存及其它线程工作内存之间是相互隔离的。那么,在并发情况下,可能会出现线程A操作更新变量后,还未刷新数据到主内存时,此时线程B读取该变量数据,导致数据与实际不一致的情况。而volatile就是为了保证操作变量在内存中的可见性,简单来说,就是任何线程的写操作都会立即刷新到主内存中,并且让那些缓存了该变量的线程内的该变量数据清空,重新从主内存中读取最新数据。

注意:volatile保证可见性,但不保证线程安全性,因为它不能保证数据的操作时原子性。可以使用synchronize对数据上锁保证原子性,或者使用Atomic包下的原子类来替换操作,它底层利用了CAS算法保证原子性。

除了保证内存的可见性,volatile还能防止JVM进行指令重排优化。指令重排是指编译器和 CPU 会在保证程序输出结果一致的情况下,会对代码进行重排序,从指令优化角度提升性能。而指令重排序可能会带来一个不好的结果,导致 CPU 的高速缓存和内存中数据的不一致。使用volatile之后可以保证业务的逻辑顺序正确性。比较经典的应用场景就是双重懒加载的单例模式了,防止拿到未初始化的对象。(new操作不是原子性动作)

volatile 在 Java 并发应用场景有很多,比如像 Atomic 包中的 value、以及 AbstractQueuedLongSynchronizer 中的 state 都是被定义为 volatile 来用于保证内存可见性。

垃圾回收

死亡对象判断方法

  • 引用计数法——对象有引用,该对象的引用计数+1,引用失效-1。引用计数为0时代表可清理回收。缺点:无法解决循环引用
  • 可达性分析法——大部分主流语言其实基本都是采用这种算法,可达性算法是通过一个 GC Roots 的对象向下搜索,整个搜索路径被称为一个引用链,当一个对象到 GC Roots 没有任何引用链的时候,则认为这个对象可以被回收。GC Roots对象主要有两种:1.方法区中静态变量所引用的对象 2.虚拟机栈中所引用的对象。

垃圾收集算法

  • 标记清除算法——标记出所有不需要回收的对象,回收未被标记的对象。这是最基础的一种收集算法。缺点:效率问题以及空间问题(产生不连续的碎片)。
  • 标记整理算法——先整理,所有存活对象向一端移动,再清理回收,避免不连续的碎片问题。
  • 标记复制算法——它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
  • 分代收集算法——当前虚拟机的垃圾收集都采用分代收集算法。可以根据新生代、老年代采取不同的收集算法。


评论