重新 equals 一定要重新 hashCode
并发编程三要素
- 原子性,不可分割
- 有序性, 程序执行顺序和代码顺序保持一致
- 可见性【可用性】, 一个线程对共享变量的修改,另一个线程立马能看到
epoll 和 poll
- select 模型,用数组存储 socket连接文件描述符,容量固定,需要轮询判断是否有IO操作 $O(n)$
- Poll 模型, 使用链表存储 socket文件描述符,克服了 数组容量默认1024的限制,同样需要轮询
- epoll 模型,使用事件通知模型,发生IO 事件时, 应用程序才进行IO操作,不需要主动去轮询 判断有没有IO操作
ArrayList 缩容机制
HashMap 是没有缩容机制的。 ArrayList 有
但是 ArrayList 不会自动缩容,要手动调用 trimToSize(), remove和 clear 都不会自动改变elementData的长度,
只会将对应元素设置为 null,以便垃圾收集器回收掉不使用的元素,节省内存
注意ArrayList 是 1.5倍库容,hashMap 2倍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++;
//判断当前容量与数组长度的大小关系
if (size < elementData.length) {
//如果size小于elementData.length,则将数组拷贝到长度为size的数组中,如果size==0,则将elementData 置为空数组,{}
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
|
concurrentHashMap 扩容机制
数组的长度大于等于64 并且 链表长度大于8 转到 红黑树,
红黑树节点数量小于 6 转为链表
golang,java , python的垃圾回收机制
参考文档
golang gc
Golang 的三色标记法
golang 的垃圾回收(GC)是基于标记清扫算法,这种算法需要进行 STW(stop the world),这个过程就会导致程序是卡顿的,频繁的 GC 会严重影响程序性能. golang 在此基础上进行了改进,通过三色标记清扫法与写屏障来减少 STW 的时间.
三色标记法的流程如下,它将对象通过白、灰、黑进行标记
1.所有对象最开始都是白色.
2.从 root 开始找到所有可达对象,标记为灰色,放入待处理队列。
3.历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色。
4.循环步骤3直到灰色队列为空为止,此时所有引用对象都被标记为黑色,所有不可达的对象依然为白色,白色的就是需要进行回收的对象。
三色标记法相对于普通标记清扫,减少了 STW 时间. 这主要得益于标记过程是 “on-the-fly” 的,在标记过程中是不需要 STW 的,它与程序是并发执行的,这就大大缩短了 STW 的时间.
作者:不能吃的坚果j
链接:https://www.jianshu.com/p/e620cf7c9120
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
ThreadLocal 原理
每个 Thread 对象都有一个 threadLocalMap
如何理解 volatile 关键字
2.可见性
对于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
3.有序性
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
另外,Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。
双亲委派机制
怎么实现 classLoader
参考文档
3.1.2 什么时候需要自己实现类加载器
当JDK提供的类加载器实现无法满足我们的需求时,才需要自己实现类加载器。
现有应用场景:OSGi、代码热部署等领域。
另外,根据上述类加载器的作用,可能有以下几个场景需要自己实现类加载器 当需要在自定义的目录中查找class文件时(或网络获取) class被类加载器加载前的加解密(代码加密领域)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package com.lordx.sprintbootdemo.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 自定义ClassLoader
* 功能:可自定义class文件的扫描路径
* @author zhiminxu
*/
// 继承ClassLoader,获取基础功能
public class TestClassLoader extends ClassLoader {
// 自定义的class扫描路径
private String classPath;
public TestClassLoader(String classPath) {
this.classPath = classPath;
}
// 覆写ClassLoader的findClass方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
// getDate方法会根据自定义的路径扫描class,并返回class的字节
byte[] classData = getDate(name);
if (classData == null) {
throw new ClassNotFoundException();
} else {
// 生成class实例
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getDate(String name) {
// 拼接目标class文件路径
String path = classPath + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
try {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int num = 0;
while ((num = is.read(buffer)) != -1) {
stream.write(buffer, 0 ,num);
}
return stream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
|
使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.lordx.sprintbootdemo.classloader;
public class MyClassLoader {
public static void main(String[] args) throws ClassNotFoundException {
// 自定义class类路径
String classPath = "/Users/zhiminxu/developer/classloader";
// 自定义的类加载器实现:TestClassLoader
TestClassLoader testClassLoader = new TestClassLoader(classPath);
// 通过自定义类加载器加载
Class<?> object = testClassLoader.loadClass("ClassLoaderTest");
// 这里的打印应该是我们自定义的类加载器:TestClassLoader
System.out.println(object.getClassLoader());
}
}
|