烫烫烫个喵啊

老裴的博客


  • 首页

  • 全部文章

  • 分类

  • 标签

  • 关于

  • 归档

探究Java对象的大小

发表于 2020-11-27 更新于 2021-07-29 分类于 Java 阅读次数:

对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

本文不讨论对象头中里面有什么,在32位系统中,对象头占8字节;在64位系统中(未开启指针压缩),对象头占16字节。

第二部分实例数据就是对象真正存储的有效数据,也就是咱们在类中定义的变量。之前在这篇 为什么short、byte会被提升为int,及基本类型的真实大小 文章中讨论过,short、byte、boolean 等类型在栈中实际也都是占用4字节,但是在堆中不必哦,例如 byte 数组,每个元素就占一字节,在其它对象中,byte也只占一字节。

第三部分对齐填充并不一定是必需存在的,仅仅是占位符的作用。由于虚拟机都要求对象起始地址必须是8字节的整数倍,所以对象的大小也必须是8字节的整数倍。当对象实例数据部分没有对齐时,就需要对齐填充来补全。(64位开启指针压缩后,对象头只有12字节,也可能会对齐填充,哪不齐就填哪,不是一定在末尾)

JOL工具

本文使用 JOL 工具测量对象的大小,JOL全称为Java Object Layout,是分析JVM中对象布局的工具,添加依赖:

1
implementation("org.openjdk.jol:jol-core:0.14")

使用方式,可以传 类 或者 实例对象。

1
2
3
System.out.println(ClassLayout.parseInstance(yourObject).toPrintable());
// or
System.out.println(ClassLayout.parseClass(Test.class).toPrintable());

影响对象大小的启动参数

这节会讨论一些会影响对象大小的启动参数,所有启动参数的解释都可以在源码中找到: http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/tip/src/share/vm/runtime/globals.hpp?utm_source=ld246.com

CompactFields

-XX:+CompactFields :在前一个字段之间的空白处分配非静态字段,默认开启。

开启该参数后,比较窄的变量可能会打破原有顺序,插入到更前的空隙位置中。

1
2
3
4
5
6
// 测试类
class A {
private boolean a;
private char b;
private long e;
}

开启CompactFields

关闭CompactFields

UseCompressedOops

由于在32位系统中,最大获取的空间地址为 4GB,对于有些应用而言不够,因此需要使用64位系统。

之前说了,在64位系统中,对象头所占空间更大,有16字节,不仅如此,原本占4字节的对象指针在64位系统中(未开启指针压缩)也膨胀了一倍,变为8字节,因此在64位系统下,会着实浪费不少内存空间。

此时,我们就可以开启指针压缩了。
-XX:+UseCompressedOops:开启指针压缩,只对 64 位系统有效,默认开启

以下是测试类A

1
2
3
4
5
6
7
8
9
10
class A {
private short a;
private char b;
private long e;
private Object f;

public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(new A()).toPrintable());
}
}

类A 不开启指针压缩

类A 开启指针压缩

再看看数组对象的压缩情况:

1
2
3
public static void main(String[] args) {
System.out.println(ClassLayout.parseInstance(new Object[10]).toPrintable());
}

数组对象不开启指针压缩

数组对象开启指针压缩
发现开启压缩后,对象头的大小由20字节(比普通对象大是因为要多存储数组大小)变为16字节,且少了一个4字节的对齐填充。

64位系统下开启指正压缩后,有以下变化:

  • 对象头由 16 字节变为 12 字节。
  • 对象引用由 8 字节变为 4 字节。
  • 数组对象的对象头由20字节变为16字节。(由于还少了一次对齐填充,能压缩8字节对象头)

Spring——详解AutowiredAnnotationBeanPostProcessor

发表于 2020-10-31 更新于 2021-07-29 分类于 Spring 阅读次数:

AutowiredAnnotationBeanPostProcessor 是一个非常重要的后置处理器,主要用于为 bean 填充属性。

类图如下:
image.png

该处理器实现了 InstantiationAwareBeanPostProcessor 接口,最重要的就是 postProcessProperties 方法,用于填充 bean 的属性。

构造器

该类只有一个构造方法,该方法设置了 AutowiredAnnotationBeanPostProcessor 能够支持哪些注解,默认有 Autowired、Value、和 Inject, Inject 则是 JSR-330 的标准。

1
2
3
4
5
6
7
8
9
10
11
12
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
依赖注入 populateBean 方法调用的主入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 寻找需要自动装配的元数据
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 为元数据注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
寻找需要自动装配的元数据

简而言之,就是先尝试从缓存拿,拿不到就去构建,构建完再放入缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
构建需要自动装配的元数据
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}

List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;

do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

// 处理每一个字段
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 在字段上寻找自动装配的注解
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
// 如果没有注解,就去找下一个字段
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
// 不支持自动装配静态属性
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判断这个属性是否必须装配
boolean required = determineRequiredStatus(ann);
// 添加到当前元素列表
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});

// 处理每一个方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 静态方法不支持自动装配
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
});

elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
// 递归寻找父类的字段和方法
while (targetClass != null && targetClass != Object.class);

return InjectionMetadata.forElements(elements, clazz);
}

@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
注入
1
2
3
4
5
6
7
8
9
10
11
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
Collection<InjectionMetadata.InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectionMetadata.InjectedElement element : elementsToIterate) {
// 为每个元素注入,元素可能是字段属性,也可能是方法,两者都继承了 InjectedElement,多态。
element.inject(target, beanName, pvs);
}
}
}
按属性注入

流程很清晰,就是获取依赖的 bean,用反射赋值给属性。

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
54
55
56
57
58
59
60
61
62
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {

private final boolean required;

private volatile boolean cached = false;

@Nullable
private volatile Object cachedFieldValue;

public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 解析依赖,如果依赖的bean 还未实例化,就实例化,因此此处会有循环依赖的问题
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
// 为这个属性注入值
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
按方法注入

也就是常说的 set注入。
流程也比较清晰,找到方法上的所有参数,依次获取 bean,最后把所有参数传进去,反射调用对象的方法。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (checkPropertySkipping(pvs)) {
return;
}
Method method = (Method) this.member;
Object[] arguments;
if (this.cached) {
// Shortcut for avoiding synchronization...
arguments = resolveCachedArguments(beanName);
}
else {
int argumentCount = method.getParameterCount();
arguments = new Object[argumentCount];
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
// 依次解析依赖
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
}
if (arguments != null) {
try {
// 反射调用方法
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}

refresh过程——invokeBeanFactoryPostProcessors(beanFactory)

发表于 2020-09-23 更新于 2021-07-29 分类于 Spring 阅读次数:

invokeBeanFactoryPostProcessors 在 Spring 启动流程中,是非常重要的一个步骤,此阶段会实例化并调用所有注册的 BeanFactoryPostProcessor beans。

方法有两个入参,分别是 工厂 beanFactory 和 初始的List<BeanFactoryPostProcessor>。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();

if (beanFactory instanceof BeanDefinitionRegistry) {
// beanFactory 默认是 DefaultListableBeanFactory 类型,实现了 DefaultListableBeanFactory 接口,会走进这个条件里
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

// beanFactoryPostProcessors 是方法传进来的参数,里面有三个BeanFactoryPostProcessor,他们对本文不重要。判断类型如果是 BeanDefinitionRegistryPostProcessor 就调用 postProcessBeanDefinitionRegistry 方法,并添加到 registryProcessor 中;如果不是,就先添加到 regularPostProcessors 中。之后会一起调用这两个集合中 BeanFactoryPostProcessor实例的postProcessBeanFactory方法。
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先获取所有实现了 PriorityOrdered 优先级接口的 BeanDefinitionRegistryPostProcessor,这个接口有能力添加 beanDefinition 到 beanFactory 中,这次只会找到一个 ConfigurationClassPostProcessor ,这是Spring最重要的一个处理器,它会找到 basePackages 下的所有 bean 的候选类,生成 beanDefinition 并添加到 beanFactory 中。下面会详谈这个处理器。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 创建这个 bean
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 标记这个处理器已经执行过,防止重复执行
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 下面两行代码会出现很多次,把 BeanDefinitionRegistryPostProcessor 放到一个list 里是为了最后调用它们实现 BeanFactoryPostProcessor 接口的方法。因此,BeanDefinitionRegistryPostProcessor 的执行时机是早于所有的 BeanFactoryPostProcessor 的。
registryProcessors.addAll(currentRegistryProcessors);
// 因此调用 BeanDefinitionRegistryPostProcessor 的接口
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 其次,获取实现了 Ordered 接口的 BeanDefinitionRegistryPostProcessor, 和上面一样的流程,只是优先级的区别罢了。
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 最后,执行那些不带优先级注解的 BeanDefinitionRegistryPostProcessors 直到不再出现新的 BeanDefinitionRegistryPostProcessor 。为什么会出现呢,因为 BeanDefinitionRegistryPostProcessor 可以注册 BeanDefinitionRegistryPostProcessor 类型的 beanDefinition,所以就要用while循环不停地寻找了。
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 出现了新的 BeanDefinitionRegistryPostProcessor, 标记为已执行,并且设置需要继续循环
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}

// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
// 走到了这里的时候,所有的 BeanDefinitionRegistryPostProcessor 已经调用完其 postProcessBeanDefinitionRegistry() 方法了,这些处理器也都存了下来。由于 BeanDefinitionRegistryPostProcessors 还继承了 BeanFactoryPostProcessor ,因此还需要调用所有 BeanDefinitionRegistryPostProcessor 的 postProcessBeanFactory() 方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}

else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// 下面的流程比较简单,找出所有的 BeanFactoryPostProcessor ,并按照其优先级依次调用其 postProcessBeanFactory() 方法。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}

// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}

BeanDefinitionRegistryPostProcessor

在上面的代码中,逻辑比较清晰,主要有两步:找出所有BeanDefinitionRegistryPostProcessor,并按优先级依次调用;找出所有 BeanFactoryPostProcessor 并按优先级依次调用。

那 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 有什么区别呢?这父子俩源码如下:

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

public interface BeanFactoryPostProcessor {

/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}


/**
* Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
* the registration of further bean definitions <i>before</i> regular
* BeanFactoryPostProcessor detection kicks in. In particular,
* BeanDefinitionRegistryPostProcessor may register further bean definitions
* which in turn define BeanFactoryPostProcessor instances.
*
* @author Juergen Hoeller
* @since 3.0.1
* @see org.springframework.context.annotation.ConfigurationClassPostProcessor
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,根据源码,其执行时机也是早于 BeanFactoryPostProcessor 的,在 自定义BeanFactoryPostProcessor的错误做法 中有介绍过 BeanFactoryPostProcessor 接口。

简而言之, BeanFactoryPostProcessor 允许你修改 BeanDefinition,BeanDefinitionRegistryPostProcessor 允许你动态的注册 BeanDefinition。

阅读全文 »

自定义BeanFactoryPostProcessor的错误做法

发表于 2020-09-22 更新于 2021-07-29 分类于 Spring 阅读次数:

BeanFactoryPostProcessor 介绍

Spring提供了很多个扩展点,包括BeanFactoryPostProcessor、BeanPostProcessor、Aware,今天主要讲述下 BeanFactoryPostProcessor 的功能及错误用法。

BeanFactoryPostProcessor 能做什么?先贴下源代码。

阅读全文 »

Spring三种初始化bean的方式及其顺序

发表于 2020-09-15 更新于 2021-07-29 分类于 Spring 阅读次数:

Spring中有三种初始化Bean的方式:

  • 对初始化方法添加注解 @PostConstruct
  • 实现 InitializingBean 接口
  • 实现 SmartInitializingSingleton 接口

首先要测试三种初始化的执行顺序,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class InitTest implements InitializingBean, SmartInitializingSingleton {

@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet");
}

@Override
public void afterSingletonsInstantiated() {
System.out.println("afterSingletonsInstantiated");
}
}

image.png

执行顺序显而易见,接下开是要说下这三种方式都是在什么地方进行执行的。

阅读全文 »

Java锁优化

发表于 2020-09-05 更新于 2021-07-29 分类于 Java 阅读次数:

自旋锁与自适应锁

  当两个线程都需要访问某个共享资源时,常用互斥同步(synchronized)来保证并发正确性,即共享数据在同一时刻只被一个线程使用,其余线程暂时挂起(即Java中的线程阻塞状态)。但假如共享数据的锁定只会持续很短的时间,为了这段时间去挂起和恢复线程并不划算,因此可以让后面请求锁的线程“稍等一下”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这就是自旋锁。

阅读全文 »

Java线程

发表于 2020-09-02 更新于 2021-07-29 分类于 Java 阅读次数:

线程的实现

  实现线程主要有 3 种方式:使用内核线程实现、使用用户线程实现、使用用户线程加轻量级进程混合实现。

1.使用内核线程实现

  内核线程(Kernel-Level Thread,KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多个任务,支持多线程的内核叫做多线程内核。

  程序一般不会直接去使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(Light Weight Process,LWP),也就是我们通常意义上所讲的线程。由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。这种轻量级进程和内核线程之间 1 : 1 的关系称为一对一的线程模型,如下图。

image.png

  由于是基于内核线程实现的,所以各种线程操作,如创建、析构及同步,都需要进行系统调用。而系统调用的代价相对较高,需要在用户态和内核态中来回切换,其次,每个轻量级进程都需要一个内核线程的支持,因此轻量级进程要消耗一定的内核资源(如内核线程的栈空间),因此,一个系统支持的轻量级进程的数量是有限的。

  Sun JDK 的 windows 版与 Linux 版都是使用此模型实现的。

2.使用用户线程实现

  广义上讲,一个线程只要不是内核线程,就可以认为是用户线程(User Thread,UT),那轻量级进程也属于用户线程,但轻量级进程的实现始终是建立在内核之上的,许多操作都要进行系统调用,效率会受到限制。

  狭义上的用户线程是指完全建立在用户控件的线程库上,系统内核不能感知线程存在,用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。这种线程是不需要切换到内核态,因此操作时非常快速且低消耗的。这种实现可以支持规模更大的线程数量,这种进程与用户线程之间 1 : N 的关系称为一对多的线程模型。

image.png

  使用用户线程的优势在于不需要系统内核支援,劣势也在于没有系统内核的支援,所有的线程操作都需要用户程序自己处理,创建、切换、调度、销毁都非常复杂。现在使用蔗渣模型的程序越来越少了,Ruby、Java曾经使用过用户线程,最终又放弃了它。

3.使用用户线程加轻量级进程混合实现

  许多Unix操作系统,如 Solaris、HO-UX 提供了 N : M 的线程模型实现,这种模型其实就是将上面两种方式混合,如下图:
image.png

Java线程调度

  线程调度是指系统为线程分配处理器使用权的过程,主要调度方式有两种:协同式线程调度和抢占式线程调度。

  协同式调度的多线程系统中,线程的执行时间是由线程本身来控制的,线程把自己的工作执行完了后,要主动通知系统切换到另外一个线程上。这种实现的最大好处是实现简单,切换操作对于线程自己事可知的,所以没有线程同步的问题。坏处也很明显:线程执行时间不可控,甚至如果一个线程编写有问题,一直不告诉系统进行线程切换,那程序就会阻塞,甚至系统崩溃。

  如果使用抢占式调度的多线程系统,那么每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。Java使用的就是抢占式调度。

操作系统中的线(进)程状态转换

  首先要简单说下操作系统中线程的状态,其有五个状态,除了创建和终止,还有就绪、运行和阻塞状态,当发生 I/O 或其它事件时,会从运行状态转变为阻塞状态,当 I/O 结束时,便从阻塞状态转变为就绪状态,此时只要有 CPU 时间片就可以立即变为运行状态。
进程转换状态

不难发现操作系统中的线程状态都是围绕着 CPU 来说的

Java线程状态转换

  Java线程定义了 6 种线程状态:

  • 新建(New):创建后尚未启动的线程处于这种状态。
  • 运行(Runbale):包括了操作系统线程状态中的 Running 和 Ready,也就是处于此状态的线程可能正在执行,也可能正在等待 CPU 为它分配执行时间。
  • 无限期等待(Waiting):处于这种状态的线程不会被分配 CPU 时间,它们要等待被其它线程显示地唤醒,以下方法会让线程陷入此状态:
    • 没有设置 timeout 参数的 Object.wait() 方法。
    • 没有设置 timeout 参数的 Thread.join() 方法。
    • LockSupport.park() 方法。
  • 期限等待(Timed Waiting):处于这种状态的线程不会被分配 CPU 时间,不过无须等待被其它线程显示地唤醒,在一定时间后它们会由系统自动唤醒,以下方法会让线程陷入此状态:
    • Thread.sleep() 方法。
    • 设置了 timeout 参数的 Object.wait() 方法。
    • 设置了 timeout 参数的 Thread.join() 方法。
    • LockSupport.parkNanos() 方法。
    • LockSupport.parkUntil() 方法。
  • 阻塞(Blocked):线程被阻塞了,阻塞与等待的区别是阻塞是在等待着获取到一个排它锁,这个事件将在另外一个事件放弃这个锁的时候发生,而等待状态则是在等待一段时间或唤醒动作的发送。当程序进入同步区域的时候,线程将进入阻塞状态。
  • 结束(Terminated):线程已经结束执行。

  需要注意的是,Java中的线程状态并不是与操作系统中的线程状态一一对应的。查看 Thread 类的源代码,可以看见 RUNNABLE 状态的注释中说了处于这个状态线程可能正在 JVM 中执行,也可能正在等待操作系统的某种资源。所以,RUNNABLE 状态至少包括了操作系统中的就绪和运行状态的。

1
2
3
4
5
6
7
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE

  当发生 I/O 阻塞时,操作系统概念下的线程会切换到阻塞状态,但 Java 的线程状态也有阻塞,还有无期限等待和期限等待,那此时 Java 的线程状态是什么呢?其实也还是 RUNNABLE,可以写一个简单 demo 来验证:

image.png

  同理,网络阻塞时,Java的线程状态也还是 RUNNABLE。Java 中的 RUNNABLE 实际对应了操作系统中的 就绪状态、运行状态和部分阻塞状态。看起来很混乱,那 Java 为什么要这么做呢?前面 RUNNABLE 的注释说到该状态的线程可能正在JVM中执行,也可能正在等操作系统的资源,JVM 把很多东西都看作了资源,硬盘、CPU、网卡也罢,只要有一个东西在为线程服务,那就可以理解为这个线程是在“执行”的,虽然 I/O 阻塞, CPU 不执行了,但网卡还在监听干活啊,只是暂时没有数据罢了。操作系统的线程状态都是围绕着 CPU 来说的,这与 JVM 的侧重点不同。

  如果 Java 中的线程处于期限等待、无期限等待或阻塞状态,那说明这个线程真的是一点事都不干了,而且是你自己不让它干的,例如调用了 sleep()、wait() 方法。

参考:https://my.oschina.net/goldenshaw/blog/705397

Java内存模型

发表于 2020-08-26 更新于 2022-04-10 分类于 Java 阅读次数:

主内存与工作内存

  Java内存模型规定了所有变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。三者的关系如下图。

image.png

阅读全文 »

JVM的运行期优化

发表于 2020-08-13 更新于 2021-07-29 分类于 Java 阅读次数:

  在部分的商用虚拟机中, Java 程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或者代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的效率,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler, JIT 编译器)。

阅读全文 »

运行时帧栈结构与方法调用

发表于 2020-08-06 更新于 2021-07-29 分类于 Java 阅读次数:

运行时栈帧结构

  栈帧是虚拟机用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。每一个方法从调入开始到执行完成的过程,都对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  在编译程序代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法表的 Code 属性中,因此,一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现,

阅读全文 »
1234…9

烫

技术、生活随笔
85 日志
24 分类
27 标签
GitHub Weibo
Links
  • 月灵博客
  • Bobby John's Blog
  • Zunpeng' Blog
皖ICP备18010985号-1 © 2023 烫
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v7.3.0