JVM系列-双亲委派模型

这是 JVM 系列的第六篇,见 JVM 系列

写在前面

在前面的文章里面,JVM 拿到 class 文件流之后,完成了以下的工作:

  • 校验魔数
  • 校验版本
  • 解析常量池
  • 解析字段
  • 解析方法
  • 解析属性
  • 创建与 java 对象对等的内部对象 instanceKlass,提供给 JVM 使用
  • 创建镜像类,提供给Java应用使用

要想在JVM内部创建对等的 oop-klass 对象,就必须要经过类加载器的加载过程。前面提到 java 体系中,我们了解到有 3 类类加载:引导类加载器(Bootstrap ClassLoader)扩展类加载器(Bootstrap ClassLoader)系统类加载器(Bootstrap ClassLoader)。实际上 JVM 规范只提到了 2 种,引导类加载器和自定义类加载器。当然引导类加载器事 C++ 写的,自定义类加载器用 java 语言定义。

C++ 定义的引导类加载器

C++ 的类加载器位于:hotspot/src/share/vm/classfile/classLoader.hpp,如下:

C++ 引导类加载器的定义
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
class ClassLoader: AllStatic {
public:
enum SomeConstants {
package_hash_table_size = 31 // Number of buckets
};
protected:
friend class LazyClassPathEntry;

// Performance counters
static PerfCounter* _perf_accumulated_time;
static PerfCounter* _perf_classes_inited;
static PerfCounter* _perf_class_init_time;
static PerfCounter* _perf_class_init_selftime;
static PerfCounter* _perf_classes_verified;
static PerfCounter* _perf_class_verify_time;
static PerfCounter* _perf_class_verify_selftime;
static PerfCounter* _perf_classes_linked;
static PerfCounter* _perf_class_link_time;
static PerfCounter* _perf_class_link_selftime;
static PerfCounter* _perf_class_parse_time;
static PerfCounter* _perf_class_parse_selftime;
static PerfCounter* _perf_sys_class_lookup_time;
static PerfCounter* _perf_shared_classload_time;
static PerfCounter* _perf_sys_classload_time;
static PerfCounter* _perf_app_classload_time;
static PerfCounter* _perf_app_classload_selftime;
static PerfCounter* _perf_app_classload_count;
static PerfCounter* _perf_define_appclasses;
static PerfCounter* _perf_define_appclass_time;
static PerfCounter* _perf_define_appclass_selftime;
static PerfCounter* _perf_app_classfile_bytes_read;
static PerfCounter* _perf_sys_classfile_bytes_read;

static PerfCounter* _sync_systemLoaderLockContentionRate;
static PerfCounter* _sync_nonSystemLoaderLockContentionRate;
static PerfCounter* _sync_JVMFindLoadedClassLockFreeCounter;
static PerfCounter* _sync_JVMDefineClassLockFreeCounter;
static PerfCounter* _sync_JNIDefineClassLockFreeCounter;

static PerfCounter* _unsafe_defineClassCallCounter;
static PerfCounter* _isUnsyncloadClass;
static PerfCounter* _load_instance_class_failCounter;

// First entry in linked list of ClassPathEntry instances
static ClassPathEntry* _first_entry;
// Last entry in linked list of ClassPathEntry instances
static ClassPathEntry* _last_entry;
static int _num_entries;

// Hash table used to keep track of loaded packages
static PackageHashtable* _package_hash_table;
static const char* _shared_archive;

// Info used by CDS
CDS_ONLY(static SharedPathsMiscInfo * _shared_paths_misc_info;)

// Hash function
static unsigned int hash(const char *s, int n);
// Returns the package file name corresponding to the specified package
// or class name, or null if not found.
static PackageInfo* lookup_package(const char *pkgname);
// Adds a new package entry for the specified class or package name and
// corresponding directory or jar file name.
static bool add_package(const char *pkgname, int classpath_index, TRAPS);

// Initialization
static void setup_bootstrap_meta_index();
static void setup_meta_index(const char* meta_index_path, const char* meta_index_dir,
int start_index);
static void setup_bootstrap_search_path();
static void setup_search_path(const char *class_path, bool canonicalize=false);

static void load_zip_library();
static ClassPathEntry* create_class_path_entry(const char *path, const struct stat* st,
bool lazy, bool throw_exception, TRAPS);

// Canonicalizes path names, so strcmp will work properly. This is mainly
// to avoid confusing the zip library
static bool get_canonical_path(const char* orig, char* out, int len);
public:
static int crc32(int crc, const char* buf, int len);
static bool update_class_path_entry_list(const char *path,
bool check_for_duplicates,
bool throw_exception=true);
static void print_bootclasspath();

// Timing
static PerfCounter* perf_accumulated_time() { return _perf_accumulated_time; }
static PerfCounter* perf_classes_inited() { return _perf_classes_inited; }
static PerfCounter* perf_class_init_time() { return _perf_class_init_time; }
static PerfCounter* perf_class_init_selftime() { return _perf_class_init_selftime; }
static PerfCounter* perf_classes_verified() { return _perf_classes_verified; }
static PerfCounter* perf_class_verify_time() { return _perf_class_verify_time; }
static PerfCounter* perf_class_verify_selftime() { return _perf_class_verify_selftime; }
static PerfCounter* perf_classes_linked() { return _perf_classes_linked; }
static PerfCounter* perf_class_link_time() { return _perf_class_link_time; }
static PerfCounter* perf_class_link_selftime() { return _perf_class_link_selftime; }
static PerfCounter* perf_class_parse_time() { return _perf_class_parse_time; }
static PerfCounter* perf_class_parse_selftime() { return _perf_class_parse_selftime; }
static PerfCounter* perf_sys_class_lookup_time() { return _perf_sys_class_lookup_time; }
static PerfCounter* perf_shared_classload_time() { return _perf_shared_classload_time; }
static PerfCounter* perf_sys_classload_time() { return _perf_sys_classload_time; }
static PerfCounter* perf_app_classload_time() { return _perf_app_classload_time; }
static PerfCounter* perf_app_classload_selftime() { return _perf_app_classload_selftime; }
static PerfCounter* perf_app_classload_count() { return _perf_app_classload_count; }
static PerfCounter* perf_define_appclasses() { return _perf_define_appclasses; }
static PerfCounter* perf_define_appclass_time() { return _perf_define_appclass_time; }
static PerfCounter* perf_define_appclass_selftime() { return _perf_define_appclass_selftime; }
static PerfCounter* perf_app_classfile_bytes_read() { return _perf_app_classfile_bytes_read; }
static PerfCounter* perf_sys_classfile_bytes_read() { return _perf_sys_classfile_bytes_read; }

// Record how often system loader lock object is contended
static PerfCounter* sync_systemLoaderLockContentionRate() {
return _sync_systemLoaderLockContentionRate;
}

// Record how often non system loader lock object is contended
static PerfCounter* sync_nonSystemLoaderLockContentionRate() {
return _sync_nonSystemLoaderLockContentionRate;
}

// Record how many calls to JVM_FindLoadedClass w/o holding a lock
static PerfCounter* sync_JVMFindLoadedClassLockFreeCounter() {
return _sync_JVMFindLoadedClassLockFreeCounter;
}

// Record how many calls to JVM_DefineClass w/o holding a lock
static PerfCounter* sync_JVMDefineClassLockFreeCounter() {
return _sync_JVMDefineClassLockFreeCounter;
}

// Record how many calls to jni_DefineClass w/o holding a lock
static PerfCounter* sync_JNIDefineClassLockFreeCounter() {
return _sync_JNIDefineClassLockFreeCounter;
}

// Record how many calls to Unsafe_DefineClass
static PerfCounter* unsafe_defineClassCallCounter() {
return _unsafe_defineClassCallCounter;
}

// Record how many times SystemDictionary::load_instance_class call
// fails with linkageError when Unsyncloadclass flag is set.
static PerfCounter* load_instance_class_failCounter() {
return _load_instance_class_failCounter;
}

// Load individual .class file
static instanceKlassHandle load_classfile(Symbol* h_name, TRAPS);

// If the specified package has been loaded by the system, then returns
// the name of the directory or ZIP file that the package was loaded from.
// Returns null if the package was not loaded.
// Note: The specified name can either be the name of a class or package.
// If a package name is specified, then it must be "/"-separator and also
// end with a trailing "/".
static oop get_system_package(const char* name, TRAPS);

// Returns an array of Java strings representing all of the currently
// loaded system packages.
// Note: The package names returned are "/"-separated and end with a
// trailing "/".
static objArrayOop get_system_packages(TRAPS);

// Initialization
static void initialize();
CDS_ONLY(static void initialize_shared_path();)
static void create_package_info_table();
static void create_package_info_table(HashtableBucket<mtClass> *t, int length,
int number_of_entries);
static int compute_Object_vtable();

static ClassPathEntry* classpath_entry(int n) {
ClassPathEntry* e = ClassLoader::_first_entry;
while (--n >= 0) {
assert(e != NULL, "Not that many classpath entries.");
e = e->next();
}
return e;
}

static int num_classpath_entries() {
return _num_entries;
}

#if INCLUDE_CDS
// Sharing dump and restore
static void copy_package_info_buckets(char** top, char* end);
static void copy_package_info_table(char** top, char* end);

static void check_shared_classpath(const char *path);
static void finalize_shared_paths_misc_info();
static int get_shared_paths_misc_info_size();
static void* get_shared_paths_misc_info();
static bool check_shared_paths_misc_info(void* info, int size);
static void exit_with_path_failure(const char* error, const char* message);
#endif

static void trace_class_path(outputStream* out, const char* msg, const char* name = NULL);

// VM monitoring and management support
static jlong classloader_time_ms();
static jlong class_method_total_size();
static jlong class_init_count();
static jlong class_init_time_ms();
static jlong class_verify_time_ms();
static jlong class_link_count();
static jlong class_link_time_ms();

// indicates if class path already contains a entry (exact match by name)
static bool contains_entry(ClassPathEntry* entry);

// adds a class path list
static void add_to_list(ClassPathEntry* new_entry);

// creates a class path zip entry (returns NULL if JAR file cannot be opened)
static ClassPathZipEntry* create_class_path_zip_entry(const char *apath);

// obtain package name from a fully qualified class name
// *bad_class_name is set to true if there's a problem with parsing class_name, to
// distinguish from a class_name with no package name, as both cases have a NULL return value
static const char* package_from_name(const char* const class_name, bool* bad_class_name = NULL);

// Debugging
static void verify() PRODUCT_RETURN;

// Force compilation of all methods in all classes in bootstrap class path (stress test)
#ifndef PRODUCT
protected:
static int _compile_the_world_class_counter;
static int _compile_the_world_method_counter;
public:
static void compile_the_world();
static void compile_the_world_in(char* name, Handle loader, TRAPS);
static int compile_the_world_counter() { return _compile_the_world_class_counter; }
#endif //PRODUCT
};

核心的方法:

加载类文件
1
2
// Load individual .class file
static instanceKlassHandle load_classfile(Symbol* h_name, TRAPS);

再看看 load_classfile() 函数里面的内容:

load_classfile() 函数定义
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
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
ResourceMark rm(THREAD);
const char* class_name = h_name->as_C_string();
EventMark m("loading class %s", class_name);
ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);

stringStream st;
// st.print() uses too much stack space while handling a StackOverflowError
// st.print("%s.class", h_name->as_utf8());
st.print_raw(h_name->as_utf8());
st.print_raw(".class");
const char* file_name = st.as_string();
ClassLoaderExt::Context context(class_name, file_name, THREAD);

// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
int classpath_index = 0;
ClassPathEntry* e = NULL;
instanceKlassHandle h;
{
PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
e = _first_entry;
while (e != NULL) {
stream = e->open_stream(file_name, CHECK_NULL);
if (!context.check(stream, classpath_index)) {
return h; // NULL
}
if (stream != NULL) {
break;
}
e = e->next();
++classpath_index;
}
}

if (stream != NULL) {
// class file found, parse it
ClassFileParser parser(stream);
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
TempNewSymbol parsed_name = NULL;
// Callers are expected to declare a ResourceMark to determine
// the lifetime of any updated (resource) allocated under
// this call to parseClassFile
// We do not declare another ResourceMark here, reusing the one declared
// at the start of the method
instanceKlassHandle result = parser.parseClassFile(h_name,
loader_data,
protection_domain,
parsed_name,
context.should_verify(classpath_index),
THREAD);
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm;
if (DumpSharedSpaces) {
tty->print_cr("Preload Error: Failed to load %s", class_name);
}
return h;
}

#if INCLUDE_JFR
{
InstanceKlass* ik = result();
ON_KLASS_CREATION(ik, parser, THREAD);
result = instanceKlassHandle(ik);
}
#endif

h = context.record_result(classpath_index, e, result, THREAD);
} else {
if (DumpSharedSpaces) {
tty->print_cr("Preload Warning: Cannot find %s", class_name);
}
}

return h;
}

load_classfile() 函数里面最重要的函数就是:parseClassFile()

parseClassFile() 函数定义
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
KlassHandle host_klass,
GrowableArray<Handle>* cp_patches,
TempNewSymbol& parsed_name,
bool verify,
TRAPS) {

// When a retransformable agent is attached, JVMTI caches the
// class bytes that existed before the first retransformation.
// If RedefineClasses() was used before the retransformable
// agent attached, then the cached class bytes may not be the
// original class bytes.
JvmtiCachedClassFileData *cached_class_file = NULL;
Handle class_loader(THREAD, loader_data->class_loader());
bool has_default_methods = false;
bool declares_default_methods = false;
// JDK-8252904:
// The stream (resource) attached to the instance klass may
// be reallocated by this method. When JFR is included the
// stream may need to survive beyond the end of the call. So,
// the caller is expected to declare the ResourceMark that
// determines the lifetime of resources allocated under this
// call.

ClassFileStream* cfs = stream();
// Timing
assert(THREAD->is_Java_thread(), "must be a JavaThread");
JavaThread* jt = (JavaThread*) THREAD;

PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(),
ClassLoader::perf_class_parse_selftime(),
NULL,
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::PARSE_CLASS);

init_parsed_class_attributes(loader_data);

if (JvmtiExport::should_post_class_file_load_hook()) {
// Get the cached class file bytes (if any) from the class that
// is being redefined or retransformed. We use jvmti_thread_state()
// instead of JvmtiThreadState::state_for(jt) so we don't allocate
// a JvmtiThreadState any earlier than necessary. This will help
// avoid the bug described by 7126851.
JvmtiThreadState *state = jt->jvmti_thread_state();
if (state != NULL) {
KlassHandle *h_class_being_redefined =
state->get_class_being_redefined();
if (h_class_being_redefined != NULL) {
instanceKlassHandle ikh_class_being_redefined =
instanceKlassHandle(THREAD, (*h_class_being_redefined)());
cached_class_file = ikh_class_being_redefined->get_cached_class_file();
}
}

unsigned char* ptr = cfs->buffer();
unsigned char* end_ptr = cfs->buffer() + cfs->length();

JvmtiExport::post_class_file_load_hook(name, class_loader(), protection_domain,
&ptr, &end_ptr, &cached_class_file);

if (ptr != cfs->buffer()) {
// JVMTI agent has modified class file data.
// Set new class file stream using JVMTI agent modified
// class file data.
cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source());
set_stream(cfs);
}
}

_host_klass = host_klass;
_cp_patches = cp_patches;

instanceKlassHandle nullHandle;

// Figure out whether we can skip format checking (matching classic VM behavior)
if (DumpSharedSpaces) {
// verify == true means it's a 'remote' class (i.e., non-boot class)
// Verification decision is based on BytecodeVerificationRemote flag
// for those classes.
_need_verify = (verify) ? BytecodeVerificationRemote :
BytecodeVerificationLocal;
} else {
_need_verify = Verifier::should_verify_for(class_loader(), verify);
}

// Set the verify flag in stream
cfs->set_verify(_need_verify);

// Save the class file name for easier error message printing.
_class_name = (name != NULL) ? name : vmSymbols::unknown_class_name();

cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor
// Magic value
u4 magic = cfs->get_u4_fast();
guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
"Incompatible magic value %u in class file %s",
magic, CHECK_(nullHandle));

// Version numbers
u2 minor_version = cfs->get_u2_fast();
u2 major_version = cfs->get_u2_fast();

if (DumpSharedSpaces && major_version < JAVA_1_5_VERSION) {
ResourceMark rm;
warning("Pre JDK 1.5 class not supported by CDS: %u.%u %s",
major_version, minor_version, name->as_C_string());
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"Unsupported major.minor version for dump time %u.%u",
major_version,
minor_version);
}

// Check version numbers - we check this even with verifier off
if (!is_supported_version(major_version, minor_version)) {
if (name == NULL) {
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"Unsupported class file version %u.%u, "
"this version of the Java Runtime only recognizes class file versions up to %u.%u",
major_version,
minor_version,
JAVA_MAX_SUPPORTED_VERSION,
JAVA_MAX_SUPPORTED_MINOR_VERSION);
} else {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), "
"this version of the Java Runtime only recognizes class file versions up to %u.%u",
name->as_C_string(),
major_version,
minor_version,
JAVA_MAX_SUPPORTED_VERSION,
JAVA_MAX_SUPPORTED_MINOR_VERSION);
}
return nullHandle;
}

_major_version = major_version;
_minor_version = minor_version;


// Check if verification needs to be relaxed for this class file
// Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376)
_relax_verify = relax_format_check_for(_loader_data);

// Constant pool
constantPoolHandle cp = parse_constant_pool(CHECK_(nullHandle));

int cp_size = cp->length();

cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len

// Access flags
AccessFlags access_flags;
jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;

if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
flags |= JVM_ACC_ABSTRACT;
}
verify_legal_class_modifiers(flags, CHECK_(nullHandle));
access_flags.set_flags(flags);

// This class and superclass
_this_class_index = cfs->get_u2_fast();
check_property(
valid_cp_range(_this_class_index, cp_size) &&
cp->tag_at(_this_class_index).is_unresolved_klass(),
"Invalid this class index %u in constant pool in class file %s",
_this_class_index, CHECK_(nullHandle));

Symbol* class_name = cp->unresolved_klass_at(_this_class_index);
assert(class_name != NULL, "class_name can't be null");

// It's important to set parsed_name *before* resolving the super class.
// (it's used for cleanup by the caller if parsing fails)
parsed_name = class_name;
// parsed_name is returned and can be used if there's an error, so add to
// its reference count. Caller will decrement the refcount.
parsed_name->increment_refcount();

// Update _class_name which could be null previously to be class_name
_class_name = class_name;

// Don't need to check whether this class name is legal or not.
// It has been checked when constant pool is parsed.
// However, make sure it is not an array type.
if (_need_verify) {
guarantee_property(class_name->byte_at(0) != JVM_SIGNATURE_ARRAY,
"Bad class name in class file %s",
CHECK_(nullHandle));
}

Klass* preserve_this_klass; // for storing result across HandleMark

// release all handles when parsing is done
{ HandleMark hm(THREAD);

// Checks if name in class file matches requested name
if (name != NULL && class_name != name) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_NoClassDefFoundError(),
"%s (wrong name: %s)",
name->as_C_string(),
class_name->as_C_string()
);
return nullHandle;
}

if (TraceClassLoadingPreorder) {
tty->print("[Loading %s", (name != NULL) ? name->as_klass_external_name() : "NoName");
if (cfs->source() != NULL) tty->print(" from %s", cfs->source());
tty->print_cr("]");
}
#if INCLUDE_CDS
if (DumpLoadedClassList != NULL && cfs->source() != NULL && classlist_file->is_open()) {
// Only dump the classes that can be stored into CDS archive
if (SystemDictionaryShared::is_sharing_possible(loader_data)) {
if (name != NULL) {
ResourceMark rm(THREAD);
classlist_file->print_cr("%s", name->as_C_string());
classlist_file->flush();
}
}
}
#endif

u2 super_class_index = cfs->get_u2_fast();
instanceKlassHandle super_klass = parse_super_class(super_class_index,
CHECK_NULL);

// Interfaces
u2 itfs_len = cfs->get_u2_fast();
Array<Klass*>* local_interfaces =
parse_interfaces(itfs_len, protection_domain, _class_name,
&has_default_methods, CHECK_(nullHandle));

u2 java_fields_count = 0;
// Fields (offsets are filled in later)
FieldAllocationCount fac;
Array<u2>* fields = parse_fields(class_name,
access_flags.is_interface(),
&fac, &java_fields_count,
CHECK_(nullHandle));
// Methods
bool has_final_method = false;
AccessFlags promoted_flags;
promoted_flags.set_flags(0);
Array<Method*>* methods = parse_methods(access_flags.is_interface(),
&promoted_flags,
&has_final_method,
&declares_default_methods,
CHECK_(nullHandle));
if (declares_default_methods) {
has_default_methods = true;
}

// Additional attributes
ClassAnnotationCollector parsed_annotations;
parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle));

// Finalize the Annotations metadata object,
// now that all annotation arrays have been created.
create_combined_annotations(CHECK_(nullHandle));

// Make sure this is the end of class file stream
guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle));

if (_class_name == vmSymbols::java_lang_Object()) {
check_property(_local_interfaces == Universe::the_empty_klass_array(),
"java.lang.Object cannot implement an interface in class file %s",
CHECK_(nullHandle));
}
// We check super class after class file is parsed and format is checked
if (super_class_index > 0 && super_klass.is_null()) {
Symbol* sk = cp->klass_name_at(super_class_index);
if (access_flags.is_interface()) {
// Before attempting to resolve the superclass, check for class format
// errors not checked yet.
guarantee_property(sk == vmSymbols::java_lang_Object(),
"Interfaces must have java.lang.Object as superclass in class file %s",
CHECK_(nullHandle));
}
Klass* k = SystemDictionary::resolve_super_or_fail(class_name, sk,
class_loader,
protection_domain,
true,
CHECK_(nullHandle));

KlassHandle kh (THREAD, k);
super_klass = instanceKlassHandle(THREAD, kh());
}
if (super_klass.not_null()) {

if (super_klass->has_default_methods()) {
has_default_methods = true;
}

if (super_klass->is_interface()) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_IncompatibleClassChangeError(),
"class %s has interface %s as super class",
class_name->as_klass_external_name(),
super_klass->external_name()
);
return nullHandle;
}
// Make sure super class is not final
if (super_klass->is_final()) {
THROW_MSG_(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class", nullHandle);
}
}

// save super klass for error handling.
_super_klass = super_klass;

// Compute the transitive list of all unique interfaces implemented by this class
_transitive_interfaces =
compute_transitive_interfaces(super_klass, local_interfaces, CHECK_(nullHandle));

// sort methods
intArray* method_ordering = sort_methods(methods);

// promote flags from parse_methods() to the klass' flags
access_flags.add_promoted_flags(promoted_flags.as_int());

// Size of Java vtable (in words)
int vtable_size = 0;
int itable_size = 0;
int num_miranda_methods = 0;

GrowableArray<Method*> all_mirandas(20);

klassVtable::compute_vtable_size_and_num_mirandas(
&vtable_size, &num_miranda_methods, &all_mirandas, super_klass(), methods,
access_flags, class_loader, class_name, local_interfaces,
CHECK_(nullHandle));

// Size of Java itable (in words)
itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(_transitive_interfaces);

FieldLayoutInfo info;
layout_fields(class_loader, &fac, &parsed_annotations, &info, CHECK_NULL);

int total_oop_map_size2 =
InstanceKlass::nonstatic_oop_map_size(info.total_oop_map_count);

// Compute reference type
ReferenceType rt;
if (super_klass() == NULL) {
rt = REF_NONE;
} else {
rt = super_klass->reference_type();
}

// We can now create the basic Klass* for this klass
_klass = InstanceKlass::allocate_instance_klass(loader_data,
vtable_size,
itable_size,
info.static_field_size,
total_oop_map_size2,
rt,
access_flags,
name,
super_klass(),
!host_klass.is_null(),
CHECK_(nullHandle));
instanceKlassHandle this_klass (THREAD, _klass);

assert(this_klass->static_field_size() == info.static_field_size, "sanity");
assert(this_klass->nonstatic_oop_map_count() == info.total_oop_map_count,
"sanity");

// Fill in information already parsed
this_klass->set_should_verify_class(verify);
jint lh = Klass::instance_layout_helper(info.instance_size, false);
this_klass->set_layout_helper(lh);
assert(this_klass->oop_is_instance(), "layout is correct");
assert(this_klass->size_helper() == info.instance_size, "correct size_helper");
// Not yet: supers are done below to support the new subtype-checking fields
//this_klass->set_super(super_klass());
this_klass->set_class_loader_data(loader_data);
this_klass->set_nonstatic_field_size(info.nonstatic_field_size);
this_klass->set_has_nonstatic_fields(info.has_nonstatic_fields);
this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]);

apply_parsed_class_metadata(this_klass, java_fields_count, CHECK_NULL);

if (has_final_method) {
this_klass->set_has_final_method();
}
this_klass->copy_method_ordering(method_ordering, CHECK_NULL);
// The InstanceKlass::_methods_jmethod_ids cache
// is managed on the assumption that the initial cache
// size is equal to the number of methods in the class. If
// that changes, then InstanceKlass::idnum_can_increment()
// has to be changed accordingly.
this_klass->set_initial_method_idnum(methods->length());
this_klass->set_name(cp->klass_name_at(_this_class_index));
if (is_anonymous()) // I am well known to myself
cp->klass_at_put(_this_class_index, this_klass()); // eagerly resolve

this_klass->set_minor_version(minor_version);
this_klass->set_major_version(major_version);
this_klass->set_has_default_methods(has_default_methods);
this_klass->set_declares_default_methods(declares_default_methods);

if (!host_klass.is_null()) {
assert (this_klass->is_anonymous(), "should be the same");
this_klass->set_host_klass(host_klass());
}

// Set up Method*::intrinsic_id as soon as we know the names of methods.
// (We used to do this lazily, but now we query it in Rewriter,
// which is eagerly done for every method, so we might as well do it now,
// when everything is fresh in memory.)
if (Method::klass_id_for_intrinsics(this_klass()) != vmSymbols::NO_SID) {
for (int j = 0; j < methods->length(); j++) {
methods->at(j)->init_intrinsic_id();
}
}

if (cached_class_file != NULL) {
// JVMTI: we have an InstanceKlass now, tell it about the cached bytes
this_klass->set_cached_class_file(cached_class_file);
}

// Fill in field values obtained by parse_classfile_attributes
if (parsed_annotations.has_any_annotations())
parsed_annotations.apply_to(this_klass);
apply_parsed_class_attributes(this_klass);

// Miranda methods
if ((num_miranda_methods > 0) ||
// if this class introduced new miranda methods or
(super_klass.not_null() && (super_klass->has_miranda_methods()))
// super class exists and this class inherited miranda methods
) {
this_klass->set_has_miranda_methods(); // then set a flag
}

// Fill in information needed to compute superclasses.
this_klass->initialize_supers(super_klass(), CHECK_(nullHandle));

// Initialize itable offset tables
klassItable::setup_itable_offset_table(this_klass);

// Compute transitive closure of interfaces this class implements
// Do final class setup
fill_oop_maps(this_klass, info.nonstatic_oop_map_count, info.nonstatic_oop_offsets, info.nonstatic_oop_counts);

// Fill in has_finalizer, has_vanilla_constructor, and layout_helper
set_precomputed_flags(this_klass);

// reinitialize modifiers, using the InnerClasses attribute
int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle));
this_klass->set_modifier_flags(computed_modifiers);

// check if this class can access its super class
check_super_class_access(this_klass, CHECK_(nullHandle));

// check if this class can access its superinterfaces
check_super_interface_access(this_klass, CHECK_(nullHandle));

// check if this class overrides any final method
check_final_method_override(this_klass, CHECK_(nullHandle));

// check that if this class is an interface then it doesn't have static methods
if (this_klass->is_interface()) {
/* An interface in a JAVA 8 classfile can be static */
if (_major_version < JAVA_8_VERSION) {
check_illegal_static_method(this_klass, CHECK_(nullHandle));
}
}

// Allocate mirror and initialize static fields
java_lang_Class::create_mirror(this_klass, class_loader, protection_domain,
CHECK_(nullHandle));

// Generate any default methods - default methods are interface methods
// that have a default implementation. This is new with Lambda project.
if (has_default_methods ) {
DefaultMethods::generate_default_methods(
this_klass(), &all_mirandas, CHECK_(nullHandle));
}

ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()),
false /* not shared class */);

if (TraceClassLoading) {
ResourceMark rm;
// print in a single call to reduce interleaving of output
if (cfs->source() != NULL) {
tty->print("[Loaded %s from %s]\n", this_klass->external_name(),
cfs->source());
} else if (class_loader.is_null()) {
Klass* caller =
THREAD->is_Java_thread()
? ((JavaThread*)THREAD)->security_get_caller_class(1)
: NULL;
// caller can be NULL, for example, during a JVMTI VM_Init hook
if (caller != NULL) {
tty->print("[Loaded %s by instance of %s]\n",
this_klass->external_name(),
InstanceKlass::cast(caller)->external_name());
} else {
tty->print("[Loaded %s]\n", this_klass->external_name());
}
} else {
tty->print("[Loaded %s from %s]\n", this_klass->external_name(),
InstanceKlass::cast(class_loader->klass())->external_name());
}
}

if (TraceClassResolution) {
ResourceMark rm;
// print out the superclass.
const char * from = this_klass()->external_name();
if (this_klass->java_super() != NULL) {
tty->print("RESOLVE %s %s (super)\n", from, InstanceKlass::cast(this_klass->java_super())->external_name());
}
// print out each of the interface classes referred to by this class.
Array<Klass*>* local_interfaces = this_klass->local_interfaces();
if (local_interfaces != NULL) {
int length = local_interfaces->length();
for (int i = 0; i < length; i++) {
Klass* k = local_interfaces->at(i);
InstanceKlass* to_class = InstanceKlass::cast(k);
const char * to = to_class->external_name();
tty->print("RESOLVE %s %s (interface)\n", from, to);
}
}
}

// preserve result across HandleMark
preserve_this_klass = this_klass();
}

JFR_ONLY(INIT_ID(preserve_this_klass);)

// Create new handle outside HandleMark (might be needed for
// Extended Class Redefinition)
instanceKlassHandle this_klass (THREAD, preserve_this_klass);
debug_only(this_klass->verify();)

// Clear class if no error has occurred so destructor doesn't deallocate it
_klass = NULL;
return this_klass;
}

parseClassFile() 函数就是我们类加载中,解析 class 字节流,生成 instanceKlass C++ 对等对象这部分的逻辑和流程,可参考前面的文章。

Java 语言定义的类加载器

Java语言定义的类加载器就是 JDK 核心类库中的类:java.lang.ClassLoader,jdk8 中的加载器类的关系如下图:
JDK加载器类之间的关系

AppClassLoaderExtClassLoader 都被组合在 Launcher 中。扩展类加载器和系统类加载器都最终依赖于 ClassLoader 类的 defineClassnative 方法,才能完成类加载。

3种类型的类加载器之间的关系:
类加载器

各种类加载器之间的关系并非面向对象的特性 “继承” 的关系,引导类加载器是用 C++ 写的,java 类想去继承也没有办法。上面的 JDK 类加载器也是被组合在 Launcher 类中,都是继承的 URLClassLoader 类。

所以这里可以看出,上面的 3 种类型的类加载器之间并没有面向对象中 “继承” 关系。

双亲委派机制

对于java应用来说,会用 AppClassLoader 类加载器去加载,先来看下 JDK 类加载方法调用链:

类加载方法调用链

上面的调用链中,最终还是会调用类 ClassLoader 中的 native 的方法去加载。比较重要的两个方法,一个是 loadClass()defineClass(),着重来看一下这两个方法,首先看 loadClass 方法,这个方法就是双亲委托加载机制的核心,方法源码:

ClassLoader
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
public abstract class ClassLoader {
......

// The parent class loader for delegation
private final ClassLoader parent;
......
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

......
}

我们知道,系统类加载器的父加载器是扩展类加载器,扩展类加载器的父类加载器是引导类加载器。java 应用类在系统类加载器(AppClassLoader)加载后,会进入到 ClassLoader 中的 loadClasss() 方法,在方法中:

  1. 首先判断有没有加载过该类
  2. AppClassLoader加载时,它的代理类加载器即 parentExtClassLoaderparent 不为空,继续嵌套调用 loaderClass 方法,这时候的 this 指向的是 ExtClassLoader,它的代理类是 C++ 引导类,所以在 java 里面是 null,就会进入方法 findBootstrapClassOrNull,这个方法调用的 native 的引导类加载的。
  3. 在上面的做操作后,如果还是没有找到该类的话,就会调用方法 findClass,这个方法在 ClassLoader 类中是留给子类实现的,在 URLClassLoader 中实现了该方法,最终还是会调用 ClassLoader 中的 defineClass 方法,而 ClassLoader 中的 defineClass 方法最后调用的是 native 系列的 defineClass* 方法,如下:
    native 系列的 defineClass 方法
    1
    2
    3
    4
    5
    6
    7
    8
    public abstract class ClassLoader {
    private native Class<?> defineClass0(String name, byte[] b, int off, int len, ProtectionDomain pd);

    private native Class<?> defineClass1(String name, byte[] b, int off, int len, ProtectionDomain pd, String source);

    private native Class<?> defineClass2(String name, java.nio.ByteBuffer b, int off, int len, ProtectionDomain pd,
    String source);
    }

所以在 JDK 中的类加载器,先找App类加载器加载,然后系统类加载器找扩展类加载器加载,扩展类加载器找引导类加载器加载,如果Ext加载器加载和Bootstrap类加载器都没有找到该类的话,那么App类加载器完成该类的加载,一级一级的往上委托,如果都不行,就自己加载,这样就保证了所有的类都能保证同一个类加载器。这就是双亲委托机制。

从上面的代码可以看到,如果想要自定义一个类加载器,并且打破双亲委派机制,那么需要重写类 ClassLoaderfindClass 方法。可以来看一个例子:
首先定义一个 Hello.java 类,代码:

Hello.java
1
2
3
4
5
6
7
package top.caolizhi.example.jvm.classloader;

public class Hello {
public void helloClassLoader() {
System.out.println("hello, class loader: " + this.getClass().getClassLoader());
}
}

再定义一个类加载器,继承 ClassLoader 类,并且重写方法 findClass。如下:

CLZClassLoader.java
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
public class CLZClassLoader extends ClassLoader {

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {

File f = new File("E:/Workspaces/Git/caolizhi/jvm/target/classes/", name.replace(".", "/").concat(".class"));
Class<?> clazz = null;
try {
FileInputStream fileInputStream = new FileInputStream(f);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int b;
while ((b = fileInputStream.read()) != -1) {
byteArrayOutputStream.write(b);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
fileInputStream.close();
clazz = defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return clazz;
}

public static void main(String[] args) {
CLZClassLoader clzClassLoader = new CLZClassLoader();
try {
System.out.println("系统类加载器:");
Class<?> clazz1 = clzClassLoader.loadClass("top.caolizhi.example.jvm.classloader.Hello");
Hello hello = (Hello) clazz1.newInstance();
hello.helloClassLoader();
System.out.println("=====================================================================");
System.out.println("自定义类加载器:");
Class<?> clazz = clzClassLoader.findClass("top.caolizhi.example.jvm.classloader.Hello");
Object o = clazz.getDeclaredConstructor().newInstance();
// Hello o = (Hello) clazz.getDeclaredConstructor().newInstance(); // 强转就会报错,因为不是同一个类加载器加载的,JVM 认为不是同一个类。
// o.helloClassLoader();
Method method = clazz.getDeclaredMethod("helloClassLoader");
method.invoke(o,null);
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}

CLZClassLoadermain 方法中,用 JDK 提供的方法 loadClass 和 自定义的 findClass 方法分别加载 Hello 类, Hello 类中的 helloClassLoader() 方法打印自己的类加载器信息。输出结果已经很明确,前者是双亲委托加载,后者并非如此。

1
2
3
4
5
系统类加载器:
hello, class loader: sun.misc.Launcher$AppClassLoader@18b4aac2
=====================================================================
自定义类加载器:
hello, class loader: top.caolizhi.example.jvm.classloader.CLZClassLoader@2f0e140b

另外不管是 JDK 的类加载器还是自定义的类加载器,最后都会调用方法 defineClass 方法,这个方法结构如下:

defineClass 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class ClassLoader {
......

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
......
}

这里面有个方法 preDefineClass 值得注意,其方法内容:

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
public abstract class ClassLoader {
......

/* Determine protection domain, and check that:
- not define java.* class,
- signer of this class matches signers for the rest of the classes in
package.
*/
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);

// Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
// relies on the fact that spoofing is impossible if a class has a name
// of the form "java.*"
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}

if (name != null) checkCerts(name, pd.getCodeSource());

return pd;
}
......
}

从它的方法体可以看到,如果加载的类的 name 是以 java. 前缀开头的,直接拒绝,抛出异常,而 java. 开头的类都是 JDK 的核心类,哈哈,机关在这里呢,所以无论如何,JDK 的核心类库,比如 java.lang.Object 都是有保护机制的,就算是自定义的类加载器也只能重写 findClass 方法,最底层的 defineClass 方法无可撼动。

参考

  1. 《深入理解 Java 虚拟机(第3版)》 周志明著.
  2. 《揭秘 Java 虚拟机:JVM 设计原理与实现》 封亚飞著.
作者

操先森

发布于

2021-12-15

更新于

2021-12-21

许可协议

评论