jvm源码解析java对象头
认真学习过java的同学应该都知道,java对象由三个部分组成:对象头,实例数据,对齐填充,这三大部分扛起了java的大旗对象,实例数据其实就是我们对象中的数据,对齐填充是由于为了规则分配内存空间,java对象大小一定是8字节的整数倍,但是我们也不能让程序员来控制吧,所以当不够8位时,会自动填充至8的整数倍,对象头记录了hash值,gc年龄,锁状态(偏向锁还会记录线程id),gc状态等等,它还保存了对象的class指针,可谓是核心中的核心,有兴趣的同学可以去看一下关于我写的对象的一些介绍:https://www.cnblogs.com/gmt-hao/p/13817564.html。那么接下来我们就从jvm层面来剖析对象头的实现,还是老规矩,先撸代码。
java作为面向对象的语言,作为代表的对象原始类名称也很有代表性:oop,我们进oop.hpp中看一下:
// oopDesc is the top baseclass for objects classes. The {name}Desc classes describe
// the format of Java objects so the fields can be accessed from C++.
// oopDesc is abstract.
// (see oopHierarchy for complete oop class hierarchy)
//
// no virtual functions allowed
...省略
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
先看一下注释,oopDesc代表所有object对象的最上层基类,至于后面一句我理解的话其实这一块的意思就是说用c++中的字段定义java对象的格式,,再看下面定义的几个字段,_mark 就是mark world,而_metadata里面有俩属性, _klass和_compressed_klass,前者就是正常的指针,而后者是压缩指针,压缩指针在1.8默认开启,可以通过-XX:-UseCompressedOops关闭,这里就不做详细赘述,反正记住都是class指针,指向具体的klass就行了,先看Klass的注释
// A Klass provides:
// 1: language level class object (method dictionary etc.)
// 2: provide vm dispatch behavior for the object
// Both functions are combined into one C++ class.
这段话的意思是Klass提供了语言级别的类对象(如方法,字典表等),vm调度行为再一个c++ 类里面
// One reason for the oop/klass dichotomy in the implementation is
// that we don't want a C++ vtbl pointer in every object. Thus,
// normal oops don't have any virtual functions. Instead, they
// forward all "virtual" functions to their klass, which does have
// a vtbl and does the C++ dispatch depending on the object's
// actual type. (See oop.inline.hpp for some of the forwarding code.)
// ALL FUNCTIONS IMPLEMENTING THIS DISPATCH ARE PREFIXED WITH "oop_"!
这段话的意思大致是解释为什么要把klass 和 对象实体分成两部分来实现,他说不希望一个c++的虚方法指针存放在每个对象中,从而普通的对象不存放任何虚方法,有着虚方法的klass可以根据对象的实际类型进行c++的调度。
现在我大概是明白了,这不就是多态吗,原来多态的实现是这么玩的,在编译时期,对象是不知道自己具体调用的方法的,而在实际运行时去klass中去找实际类型调用对应方法。
我们再看一下实际类加载的klass子类InstanceKlass:
class InstanceKlass: public Klass {
friend class VMStructs;
friend class ClassFileParser;
friend class CompileReplay; protected:
// Constructor 构造函数
InstanceKlass(int vtable_len, //虚方法表大小
int itable_len, //接口函数表大小
int static_field_size, //静态变量个数
int nonstatic_oop_map_size, //非静态变量个数
ReferenceType rt, //引用类型
AccessFlags access_flags, //当前类的访问修饰符(public private)
bool is_anonymous); //是否匿名
。。。。。。。 // See "The Java Virtual Machine Specification" section 2.16.2-5 for a detailed description
// of the class loading & initialization procedure, and the use of the states.
enum ClassState {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};
protected:
// Annotations for this class 类注解信息
Annotations* _annotations;
// Array classes holding elements of this class.
Klass* _array_klasses;
// Constant pool for this class.
ConstantPool* _constants;
// The InnerClasses attribute and EnclosingMethod attribute. The
// _inner_classes is an array of shorts. If the class has InnerClasses
// attribute, then the _inner_classes array begins with 4-tuples of shorts
// [inner_class_info_index, outer_class_info_index,
// inner_name_index, inner_class_access_flags] for the InnerClasses
// attribute. If the EnclosingMethod attribute exists, it occupies the
// last two shorts [class_index, method_index] of the array. If only
// the InnerClasses attribute exists, the _inner_classes array length is
// number_of_inner_classes * 4. If the class has both InnerClasses
// and EnclosingMethod attributes the _inner_classes array length is
// number_of_inner_classes * 4 + enclosing_method_attribute_size.
Array<jushort>* _inner_classes; // the source debug extension for this klass, NULL if not specified.
// Specified as UTF-8 string without terminating zero byte in the classfile,
// it is stored in the instanceklass as a NULL-terminated UTF-8 string
char* _source_debug_extension;
// Array name derived from this class which needs unreferencing
// if this class is unloaded.
Symbol* _array_name; // Number of heapOopSize words used by non-static fields in this klass
// (including inherited fields but after header_size()).
int _nonstatic_field_size;
int _static_field_size; // number words used by static fields (oop and non-oop) in this klass
// Constant pool index to the utf8 entry of the Generic signature,
// or 0 if none.
u2 _generic_signature_index;
// Constant pool index to the utf8 entry for the name of source file
// containing this klass, 0 if not specified.
u2 _source_file_name_index;
u2 _static_oop_field_count;// number of static oop fields in this klass
u2 _java_fields_count; // The number of declared Java fields
int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks // _is_marked_dependent can be set concurrently, thus cannot be part of the
// _misc_flags.
bool _is_marked_dependent; // used for marking during flushing and deoptimization
可以看到初始化的Klass的构造方法包含了像虚函数表大小,引用类型等等基本信息,再往下可以看到这里面字段增加了注解属性,当前常量池中保存的当前类引用,内部类等等。
说完klass,我们在聊一聊今天的重头戏mark word,我们首先还是先看一下作者的注释:
The markOop describes the header of an object.
markOop描述了一个对象头
//
// Note that the mark is not a real oop but just a word.
// It is placed in the oop hierarchy for historical reasons.
请注意mark只是一个word(32位机器上就是32个字节,64位就是64个字节)而不是一个真实对象,由于一些历史原因他被留在了oop结构中
//
// Bit-format of an object header (most significant first, big endian layout below):
//对象的字节格式采用大端模式(高位字节放低位地址)
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
第一句就点明了它作为我们这一章的主角地位,markOop描述了一个对象头,好家伙,这个才是真正的对象头,看了一圈网上的文章,基本都是在描述mark word和klass指针之类的,但是没关系,只是定义不同。
再看下面的字节格式,我们主要看一下64位系统,根据上述提供的我们看一下这4种情况:
1.未加锁但调用了hash是这样的:
2.加了偏向锁,并偏向指定线程:
3.CMS标记:
4.回收就不谈了,肯定是空的。
这里其实存在一个问题,可以看到第二种偏向锁的场景是没办法再存hash值的,那难道我加了偏向锁就不能在获取hash值了吗,答案当然是否定的,要分析这个我们先来看一段代码:
public class Response {
}
@Slf4j
public class TestHeader { static Response response = new Response();
public static void aaa(Response response) throws InterruptedException {
log.info(Thread.currentThread().getName() + "out" +ClassLayout.parseInstance(response).toPrintable()); synchronized (response){
log.info(Thread.currentThread().getName() + ClassLayout.parseInstance(response).toPrintable());
sleep(5000);
log.info(Thread.currentThread().getName());
}
} public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1"){
@SneakyThrows
@Override
public void run(){
sleep(2000);
aaa(response);
}
};
Thread t2 = new Thread("t2"){
@SneakyThrows
@Override
public void run(){
aaa(response);
}
};
t1.start();
t2.start();
t1.join();
t2.join();
}
}
这里Response是一个空对象,没有计算hash,我们看打印结果:
16:03:40.326 [t2] INFO com.example.demo.TestHeader - t2outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:03:40.330 [t2] INFO com.example.demo.TestHeader - t2com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 b0 59 1f (00000101 10110000 01011001 00011111) (525971461)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:03:42.368 [t1] INFO com.example.demo.TestHeader - t1outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 b0 59 1f (00000101 10110000 01011001 00011111) (525971461)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:03:45.331 [t2] INFO com.example.demo.TestHeader - t2
16:03:45.331 [t1] INFO com.example.demo.TestHeader - t1com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ba 16 ee 1c (10111010 00010110 11101110 00011100) (485365434)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059) //klass引用
12 4 (loss due to the next object alignment) //对齐填充
上面的对象头介绍我们可以知道,锁的标识是最后两位,而倒数第三位
我们在来介绍一下其他几个的含义:age用来记录gc年龄(由于只有4位,最多只能记录到15,因此gc年龄最大也就是15),biased_lock表示偏向锁标识,0关闭,1开启,lock标识锁状态,01偏向锁,00轻量锁,10重量锁,而当被gc标记时,后三位用来表示标记符。
然后大端模式导致我们显示出来的和想象的不一样,可以看到除了对齐填充和klass就是mark word 一共64个01,8个字节,而这8个字节按倒序排序(前8位所占的字节其实是最后一个字节),所以我们看锁标记直接看标红地方的后三位就可以了。
我们在来具体分析一下这个代码,两个线程t1和t2,t1启动后等待2秒,t2先跑,拿到锁之后歇5秒,而t1在2秒之后到达,则会进行锁竞争,我们可以看到在t2在第一次拿到锁之后,将线程id记录了下来,而t1过来抢锁之后,则由偏向锁直接升级为重量锁。
我们再试一下将休眠5s给去掉,看下执行结果:
16:45:17.873 [t2] INFO com.example.demo.TestHeader - t2outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:45:17.876 [t2] INFO com.example.demo.TestHeader - t2com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 48 27 1f (00000101 01001000 00100111 00011111) (522668037)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:45:17.876 [t2] INFO com.example.demo.TestHeader - t2
16:45:19.843 [t1] INFO com.example.demo.TestHeader - t1outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 48 27 1f (00000101 01001000 00100111 00011111) (522668037)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:45:19.844 [t1] INFO com.example.demo.TestHeader - t1com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f0 f3 ac 1f (11110000 11110011 10101100 00011111) (531428336)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
前面三次还是一样的,由于t2没有休眠,所以拿完锁直接释放了,而t1休眠2秒过来抢锁,偏向已经撤销,转为轻量锁00了。
我们再看一下刚才说的hashCode的情况:
public class TestHeader { static Response response = new Response();
public static void aaa(Response response) throws InterruptedException {
log.info(Thread.currentThread().getName() + "out" +ClassLayout.parseInstance(response).toPrintable());
response.hashCode();
log.info(Thread.currentThread().getName() + "hash" +ClassLayout.parseInstance(response).toPrintable());
synchronized (response){
log.info(Thread.currentThread().getName() + ClassLayout.parseInstance(response).toPrintable());
// sleep(5000);
}
} public static void main(String[] args) throws InterruptedException {
Thread t2 = new Thread("t2"){
@SneakyThrows
@Override
public void run(){
aaa(response);
}
};
t2.start();
t2.join();
}
这里只启动了一个线程,分别在hash计算前,计算后和加锁后打印:
16:50:19.440 [t2] INFO com.example.demo.TestHeader - t2outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:50:19.443 [t2] INFO com.example.demo.TestHeader - t2hashcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 63 bb 3f (00000001 01100011 10111011 00111111) (1069245185)
4 4 (object header) 50 00 00 00 (01010000 00000000 00000000 00000000) (80)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:50:19.444 [t2] INFO com.example.demo.TestHeader - t2com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 10 ee 1e 1f (00010000 11101110 00011110 00011111) (522120720)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
12 4 (loss due to the next object alignment)
可以看到第一次就是常规的匿名可偏向,而计算完hash之后,变为不可偏向,并计算了hash值,加锁之后也不再是偏向锁,而是直接变为了轻量锁并保存线程id,再看一下,如果已经偏向某个线程后在调用hashCode的结果:
public class TestHeader { static Response response = new Response();
public static void aaa(Response response) throws InterruptedException {
log.info(Thread.currentThread().getName() + "out" +ClassLayout.parseInstance(response).toPrintable()); synchronized (response){
log.info(Thread.currentThread().getName() + ClassLayout.parseInstance(response).toPrintable());
response.hashCode();
log.info(Thread.currentThread().getName() + "hash" +ClassLayout.parseInstance(response).toPrintable());
// sleep(5000);
}
} public static void main(String[] args) throws InterruptedException {
Thread t2 = new Thread("t2"){
@SneakyThrows
@Override
public void run(){
aaa(response);
}
};
t2.start();
t2.join();
}
}
执行结果:
16:59:12.601 [t2] INFO com.example.demo.TestHeader - t2outcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:59:12.604 [t2] INFO com.example.demo.TestHeader - t2com.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 68 40 1f (00000101 01101000 01000000 00011111) (524314629)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 16:59:12.604 [t2] INFO com.example.demo.TestHeader - t2hashcom.example.demo.Response object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 2a 12 d4 1c (00101010 00010010 11010100 00011100) (483660330)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 9f c1 00 f8 (10011111 11000001 00000000 11111000) (-134168161)
12 4 (loss due to the next object alignment)
可以看到由偏向锁直接升级为重量锁(10)。
总结:
对象头其实在我看来就是一个死的概念,更多的时在gc或者是锁甚至是以后其他的操作,在jdk源码和jvm中看到了很多对于一个int值或者其他多字节的字段进行拆解操作,比如像jdk中的读写锁,便是用高低位分别表示,,而像这里也是用了一个word表示出那么多的花样,这一篇本来是不打算写的,但是当我要写synchronized的源码分析时,写了一小段突然发现卡壳了,完全没有办法绕开它,不过这也说明了对象头的重要性吧。
对于锁的升级,从上面的例子也可以看出默认情况下为匿名可偏向(这里是默认去除偏向延迟的,可以加上-XX:BiasedLockingStartupDelay=0),当有一个线程过来时,会偏向当前线程,而多个线程交替执行(即一个线程执行完再执行下一个,永远不会出现两个线程同时在锁临界区内),则会升级为轻量锁,而多个线程竞争(两个或以上线程同时在临界区中),而在计算hash值之后,匿名偏向计算hash后加锁则升级为轻量锁,加锁后计算hash则直接升级为重量锁。
.tb_button { padding: 1px; cursor: pointer; border-right: 1px solid rgba(139, 139, 139, 1); border-left: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1) }
.tb_button.hover { borer: 2px outset #def; background-color: rgba(248, 248, 248, 1) !important }
.ws_toolbar { z-index: 100000 }
.ws_toolbar .ws_tb_btn { cursor: pointer; border: 1px solid rgba(85, 85, 85, 1); padding: 3px }
.tb_highlight { background-color: rgba(255, 255, 0, 1) }
.tb_hide { visibility: hidden }
.ws_toolbar img { padding: 2px; margin: 0 }
.tb_button { padding: 1px; cursor: pointer; border-right: 1px solid rgba(139, 139, 139, 1); border-left: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1) }
.tb_button.hover { borer: 2px outset #def; background-color: rgba(248, 248, 248, 1) !important }
.ws_toolbar { z-index: 100000 }
.ws_toolbar .ws_tb_btn { cursor: pointer; border: 1px solid rgba(85, 85, 85, 1); padding: 3px }
.tb_highlight { background-color: rgba(255, 255, 0, 1) }
.tb_hide { visibility: hidden }
.ws_toolbar img { padding: 2px; margin: 0 }
.tb_button { padding: 1px; cursor: pointer; border-right: 1px solid rgba(139, 139, 139, 1); border-left: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1) }
.tb_button.hover { borer: 2px outset #def; background-color: rgba(248, 248, 248, 1) !important }
.ws_toolbar { z-index: 100000 }
.ws_toolbar .ws_tb_btn { cursor: pointer; border: 1px solid rgba(85, 85, 85, 1); padding: 3px }
.tb_highlight { background-color: rgba(255, 255, 0, 1) }
.tb_hide { visibility: hidden }
.ws_toolbar img { padding: 2px; margin: 0 }
.tb_button { padding: 1px; cursor: pointer; border-right: 1px solid rgba(139, 139, 139, 1); border-left: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1) }
.tb_button.hover { borer: 2px outset #def; background-color: rgba(248, 248, 248, 1) !important }
.ws_toolbar { z-index: 100000 }
.ws_toolbar .ws_tb_btn { cursor: pointer; border: 1px solid rgba(85, 85, 85, 1); padding: 3px }
.tb_highlight { background-color: rgba(255, 255, 0, 1) }
.tb_hide { visibility: hidden }
.ws_toolbar img { padding: 2px; margin: 0 }
jvm源码解析java对象头的更多相关文章
- 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理
一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...
- 【JVM源码解析】模板解释器解释执行Java字节码指令(上)
本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...
- Java源码解析——Java IO包
一.基础知识: 1. Java IO一般包含两个部分:1)java.io包中阻塞型IO:2)java.nio包中的非阻塞型IO,通常称为New IO.这里只考虑到java.io包中堵塞型IO: 2. ...
- 源码解析Java Attach处理流程
前言 当Java程序运行时出现CPU负载高.内存占用大等异常情况时,通常需要使用JDK自带的工具jstack.jmap查看JVM的运行时数据,并进行分析. 什么是Java Attach 那么JVM自带 ...
- jvm源码解读--20 结合jvm源码理解 java 设计模式 模板方法
write by 张艳涛 前言: 在学习jvm之前,看过设计模式的书,知道模板方法的设计模式,今天在看java并发编程的艺术里面关于AbstractQueuedSynchronizer 用法,这个就使 ...
- ConcurrentHashMap 源码解析 -- Java 容器
ConcurrentHashMap的整个结构是一个Segment数组,每个数组由单独的一个锁组成,Segment继承了ReentrantLock. 然后每个Segment中的结构又是类似于HashTa ...
- underscore.js源码解析【对象】
// Object Functions // ---------------- // Keys in IE < 9 that won't be iterated by `for key in . ...
- Java并发包源码学习系列:阻塞队列实现之ArrayBlockingQueue源码解析
目录 ArrayBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e) ...
- Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析
目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...
随机推荐
- Networking from the container's point of view 笔记
Networking from the container's point of view Published portsThe type of network a container uses, w ...
- windows使用git bash 无法交互键盘上下键移动选择选项的解决方法
目录 遇到的问题 解决方案 1. 直接通过数字键来选择 2. 使用 winpty 来启动命令 3. 将 git bash 换成 cmd 4. 使用vscode中是bash 遇到的问题 windows使 ...
- 使用pdf2htmlEX将pdf文件转为html
https://github.com/coolwanglu/pdf2htmlEX 参考github文档,转换出来的的效果貌似很好,可以参考OFFICE 文档转换为html在线预览. pdf2swf 和 ...
- AES 逻辑
分组长度 加密逻辑 轮函数 参考:链接 字节代换 两种方法: 1.首先(将字节看做GF(28)上的元素,映射到自己的乘法逆元)换成人话就是(对多项式的逆,参考:链接): 其次,对字节做仿射变换 2 ...
- [LeetCode]313. Super Ugly Number超级丑数,丑数系列看这一道就行了
丑数系列的题看这一道就可以了 /* 和ugly number2差不多,不过这次的质因子多了,所以用数组来表示质因子的target坐标 target坐标指的是这个质因子此次要乘的前任丑数是谁 */ pu ...
- 痞子衡嵌入式:Farewell, 我的2020
-- 题图:苏州大学老校门 2020年的最后一天,痞子衡驱车300多公里从苏州赶回了苏北老家(扬州某边陲小镇),连镇铁路虽然新通车了,解决了苏南苏北多年的铁路不直通问题,但奈何痞子衡老家小镇离最近的火 ...
- Mapreduce实例--去重
数据去重"主要是为了掌握和利用并行化思想来对数据进行有意义的筛选.统计大数据集上的数据种类个数.从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重. 数据去重的最终目标是让原始数据中 ...
- 数据库分库分表容量划分建议参考阿里云DRDS原则
做分库分表的时候 一直想知道分库分表容量的最优规则有什么好的建议,以下是参考阿里云 DRDS 分库分表的规则,还是有一定的参考意义 .
- 【JavaWeb】JSP 页面
JSP 页面 简介 JSP(Java Server Pages),即 Java 的服务器页面.它的主要作用是代替 Servlet 程序回传 HTML 页面的数据,因为 Servlet 程序回传 HTM ...
- 【Redis3.0.x】数据类型
Redis3.0.x 数据类型 五大数据类型 String(字符串) string 是 redis 最基本的类型.可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value. ...