volatile

定义

volatile变量规则:对volatile变量的写入操作必须在对该变量的读操作之前执行

volatile变量规则只是一种标准,要求JVM实现保证volatile变量的偏序语义。结合程序顺序规则、传递性,该偏序语义通常表现为两个作用:

  • 保持可见性
  • 禁用重排序(读操作禁止重排序之后的操作,写操作禁止重排序之前的操作)
阅读更多

常见内存溢出错误

Exception in thread “main”: java.lang.OutOfMemoryError: Java heap space

原因:对象不能被分配到堆内存中。

Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space

原因:类或者方法不能被加载到老年代。它可能出现在一个程序加载很多类的时候,比如引用了很多第三方的库。

阅读更多

JVM内存结构

概述

img_8.png

JVM内存结构主要有三大块:·堆内存、方法区和栈·。
堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,
默认情况下年轻代按照8:1:1的比例来分配;

方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);
栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。

xx xxxxxxxx desc
堆(Heap) 线程共享 所有的对象实例以及数组都要在堆上分配。
回收器主要管理的对象。
方法区(Method Area) 线程共享 Non-Heap(非堆)
存储类信息、常量、静态变量、即时编译器编译后的代码。
方法栈(JVM Stack) 线程私有 存储局部变量表、操作栈、动态链接、方法出口,对象指针。
本地方法栈(Native Method Stack) 线程私有 为虚拟机使用到的Native 方法服务。如Java使用c或者c++编写的接口服务时,代码在此区运行。
程序计数器(Program Counter Register) 线程私有 PC寄存器(PC Register)
当前线程所执行的字节码的行号指示器。指向下一条要执行的指令。

在通过一张图来了解如何通过参数来控制各区域的内存大小
img_7.png

1
2
3
4
5
6
7
-Xms设置堆的最小空间大小
-Xmx设置堆的最大空间大小
-XX:NewSize设置新生代最小空间大小
-XX:MaxNewSize设置新生代最大空间大小
-XX:PermSize设置永久代最小空间大小
-XX:MaxPermSize设置永久代最大空间大小
-Xss设置每个线程的堆栈大小
阅读更多

java 单例模式的写法

饱汉模式

饱汉是变种最多的单例模式。我们从饱汉出发,通过其变种逐渐了解实现单例模式时需要关注的问题。

基础的饱汉

饱汉,即已经吃饱,不着急再吃,饿的时候再吃。所以他就先不初始化单例,等第一次使用的时候再初始化,即·“懒加载”·。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 饱汉
// UnThreadSafe
public class Singleton1 {
private static Singleton1 singleton = null;
private Singleton1() {
}
public static Singleton1 getInstance() {
if (singleton == null) {
singleton = new Singleton1();
}
return singleton;
}
}
阅读更多

AbstractQueuedSynchronizer

AQS (AbstractQueuedSynchronizer) 抽象类的队列式同步器

J.U.C是基于AQS实现的,AQS是一个同步器,设计模式是模板模式
核心数据结构:双向链表 + state(锁状态)
底层操作:CAS

原理概览

AQS核心思想是,如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中。

CLH:Craig、Landin and Hagersten队列,是单向链表,AQS中的队列是CLH变体的虚拟双向队列(FIFO),AQS是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配。

img.png

阅读更多