java hotspot虚拟机 - init方法

初始化方法

在编译生成class文件时,会自动产生两个方法,一个是类的初始化方法, 另一个是实例的初始化方法

:在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行

:在实例创建出来的时候调用,包括调用new操作符;调用Class或java.lang.reflect.Constructor对象的newInstance()方法;调用任何现有对象的clone()方法;通过java.io.ObjectInputStream类的getObject()方法反序列化。

init的实现

是由jvm实现的,以下是hotspot jvm实现的版本

openjdk\hotspot\src\share\vm\oops\instanceKlass.cpp
openjdk\hotspot\src\share\vm\oops\instanceKlassKlass.cpp

为什么叫

名词解释

  • oops原来不是Object Oriented Programming,实际指的是 Ordinary Object Pointer(普通对象指针)。它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象。而klass则包含 元数据和方法信息,用来描述Java类。

  • Klass

  • KlassKlass

参考

(见JVM规范8中的2.9节)

java hotspot虚拟机 - class文件

oop-klass model概述

HotSpot JVM并没有根据Java实例对象直接通过虚拟机映射到新建的C++对象,而是设计了一个oop-klass model。

当我们在写Java代码的时候,我们会面对着无数个接口,类,对象和方法。但我们有木有想过,Java中的这些对象、类和方法,在HotSpot JVM中的结构又是怎么样呢?HotSpot JVM底层都是C++实现的,那么Java的对象模型与C++对象模型之间又有什么关系呢?今天就来分析一下HotSpot JVM中的对象模型:oop-klass model,它们的源码位于openjdk-8/openjdk/hotspot/src/share/vm/oops文件夹内。

那么为何要设计这样一个一分为二的对象模型呢?这是因为HotSopt JVM的设计者不想让每个对象中都含有一个vtable(虚函数表),所以就把对象模型拆成klass和oop,其中oop中不含有任何虚函数,而klass就含有虚函数表,可以进行method dispatch。这个模型其实是参照的 Strongtalk VM 底层的对象模型。

jdk版本:openjdk-7-fcs-src-b147-27_jun_2011
源码路径:openjdk\hotspot\src\share\vm\oops\

在oopsHierarchy.hpp里定义了oop和klass各自的体系。
这是oop的体系:

typedef class oopDesc*                            oop;
typedef class   instanceOopDesc*            instanceOop;
typedef class   methodOopDesc*                    methodOop;
typedef class   constMethodOopDesc*            constMethodOop;
typedef class   methodDataOopDesc*            methodDataOop;
typedef class   arrayOopDesc*                    arrayOop;
typedef class     objArrayOopDesc*            objArrayOop;
typedef class     typeArrayOopDesc*            typeArrayOop;
typedef class   constantPoolOopDesc*            constantPoolOop;
typedef class   constantPoolCacheOopDesc*   constantPoolCacheOop;
typedef class   klassOopDesc*                    klassOop;
typedef class   markOopDesc*                    markOop;
typedef class   compiledICHolderOopDesc*    compiledICHolderOop;

概述,代码架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
klass.cpp
oop.cpp
arrayKlass.cpp
arrayOop.cpp
instanceKlass.cpp
instanceOop.cpp // #include "oops/oop.hpp"
methodKlass.cpp

// A methodOop represents a Java method.
// #include "oops/constantPoolOop.hpp"#include "oops/instanceKlass.hpp" #include "oops/oop.hpp"
methodOop.cpp

methodDataKlass.cpp
methodDataOop.cpp
objArrayKlass.cpp
objArrayOop.cpp

symbol.cpp

名词解释

  • oop:
    Ordinary Object Pointer(普通对象指针),oop.h中定义了oopDesc类(没有oop这个类)

oop* 有这个而东东啊

  • Desc:
    即Describe, {name}Desc classes describe the format of Java objects so the fields can be accessed from C++
  • oopDesc:
    oop对象的类型其实是oopDesc*。在Java程序运行的过程中,每创建一个新的对象,在JVM内部就会相应地创建一个对应类型的oop对象。各种oop类的共同基类为oopDesc类。

  • oop-klass model:

Klass

一个Klass对象代表一个类的元数据(相当于java.lang.Class对象)。它提供:
language level class object (method dictionary etc.)
provide vm dispatch behavior for the object

所有的函数都被整合到一个C++类中。
Klass对象的继承关系:xxxKlass <:< Klass <:< Metadata <:< MetaspaceObj

klass对象的布局如下:
来自klass.hpp

// A Klass is the part of the klassOop that 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. The toplevel class "Klass"
// implements purpose 1 whereas all subclasses provide extra virtual functions
// for purpose 2.

// 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 layout:
//    [header        ] klassOop
//    [klass pointer ] klassOop
//    [C++ vtbl ptr  ] (contained in Klass_vtbl)
//    [layout_helper ]
//    [super_check_offset   ] for fast subtype checks
//    [secondary_super_cache] for fast subtype checks
//    [secondary_supers     ] array of 2ndary supertypes
//    [primary_supers 0]
//    [primary_supers 1]
//    [primary_supers 2]
//    ...
//    [primary_supers 7]
//    [java_mirror   ]
//    [super         ]
//    [name          ]
//    [first subklass]
//    [next_sibling  ] link to chain additional subklasses
//    [modifier_flags]
//    [access_flags  ]
//    [verify_count  ] - not in product
//    [alloc_count   ]
//    [last_biased_lock_bulk_revocation_time] (64 bits)
//    [prototype_header]
//    [biased_lock_revocation_count]

oop

oop类型其实是oopDesc*。在Java程序运行的过程中,每创建一个新的对象,在JVM内部就会相应地创建一个对应类型的oop对象。各种oop类的共同基类为oopDesc类。

JVM内部,一个Java对象在内存中的布局可以连续分成两部分:instanceOopDesc和实例数据。instanceOopDesc和arrayOopDesc又称为对象头。

instanceOopDesc对象头包含两部分信息:Mark Word 和 元数据指针(Klass*):

// from oop.hpp
// oopDesc is abstract.
// (see oopHierarchy for complete oop class hierarchy)
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;  // mark word
  union _metadata {    // metadata
    wideKlassOop    _klass;
    narrowOop       _compressed_klass;
  } _metadata;
  ...
  • Mark word: // 存储对象的hashCode或锁信息等。
  • Klass* // 存储到对象类型数据的指针

基础知识:

C++ vtbl pointer:

虚函数

ss

参考

深入探究JVM | klass-oop对象模型研究

【java源码系列】 - System

简介

java.lang.System.java

跟踪arraycopy

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

System.c

// jdk/src/share/native/java/lang/System.c

/* Only register the performance-critical methods */
static JNINativeMethod methods[] = {
    {"currentTimeMillis", "()J",              (void *)&JVM_CurrentTimeMillis},
    {"nanoTime",          "()J",              (void *)&JVM_NanoTime},
    {"arraycopy",     "(" OBJ "I" OBJ "II)V", (void *)&JVM_ArrayCopy},
};

jvm.cpp

// src/share/vm/prims/jvm.cpp

JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
                               jobject dst, jint dst_pos, jint length))
  JVMWrapper("JVM_ArrayCopy");
  // Check if we have null pointers
  if (src == NULL || dst == NULL) {
    THROW(vmSymbols::java_lang_NullPointerException());
  }
  arrayOop s = arrayOop(JNIHandles::resolve_non_null(src));
  arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst));
  assert(s->is_oop(), "JVM_ArrayCopy: src not an oop");
  assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop");
  // Do copy
  Klass::cast(s->klass())->copy_array(s, src_pos, d, dst_pos, length, thread);
JVM_END

【java源码系列】Collection Framework

Hierarchy

1
2
3
4
5
6
7
8
9
10
11
12
13
Collection
├List
│├ArrayList
│├LinkedList
│└Vector
│ └Stack # 实现了栈的基本操作,由于涉及不够规范,目前极少使用。使用queue接口的相关实现可以完全取代它
├PriorityQueue
├Set
│├EnumSet
│├HashSet
│├LinkedHashSet
│├TreeSet
└ArrayDeque

http://www.cnblogs.com/skywang12345/p/3308498.html

compare

同步(线程安全) 随机访问 快速增删 存储空间 复杂度:增删改查,containsValue 其他语言
Array .. Yes O() 最小
ArrayList .. Yes O() .
LinkedList .. No O() . redis中的list采用双向链表实现
Vector ..
Stack
Queue

class Stack extends Vector
interface Queue extends Collection

实例场景

排队:秒杀。FIFO,
消息队列:
频繁插入:采用linkedList

【java源码系列】Map

diagram,java设计架构

来自网络 http://www.cnblogs.com/skywang12345/p/3308931.html

Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。

summary

同步(线程安全) order null key null value implementation 增删查改(按key) 查(按value) containsValue C C++(STL) python
HashMap × 无序 hash table (采用seperate chaining解决键冲突)java8采用了哈希表与红黑树结合的方法 O(1) 顺序查找 O(n) 比如redis的实现
LinkedHashmap × 按插入顺序排序 同上
Hashtable 无序 × × 同上
TreeMap 按key自定义排序 红黑树 O(log n)
WeakHashMap
EnumMap
  • HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  • HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。

sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。
Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。
结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

有没有既linked,又线程安全的Map,答案没有。因为多个线程同时操作,不同的执行顺序会产生不同的结果。所以linked的东东都应该不存在线程安全性。不能加锁吗? –by xs

底层实现

HashTable使用Enumeration,HashMap使用Iterator。
HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

6.哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
而HashMap重新计算hash值,而且用与代替求模:
int hash = hash(k);
int i = indexFor(hash, table.length);

static int hash(Object x) {
  int h = x.hashCode();

  h += ~(h << 9);
  h ^= (h >>> 14);
  h += (h << 4);
  h ^= (h >>> 10);
  return h;
}
static int indexFor(int h, int length) {
  return h & (length-1);
}
以上只是一些比较突出的区别,当然他们的实现上还是有很多不同的,比如
HashMap对null的操作

哈希冲突

java hotspot虚拟机 - SocketOutputStream

SocketOutputStream.java

就只有这一个native方法

1
2
3
4
5
6
7
8
9
/**
* Writes to the socket.
* @param fd the FileDescriptor
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
* @exception IOException If an I/O error has occurred.
*/
private native void socketWrite0(FileDescriptor fd, byte[] b, int off, int len) throws IOException;

SocketOutputStream.c

路径: openjdk\jdk\src\windows\native\java\net\SocketOutputStream.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
#include <windows.h>
#include <winsock2.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>

#include "java_net_SocketOutputStream.h"

#include "net_util.h"
#include "jni_util.h"

/************************************************************************
* SocketOutputStream
*/
static jfieldID IO_fd_fdID;

/*
* Class: java_net_SocketOutputStream
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_init(JNIEnv *env, jclass cls) {
IO_fd_fdID = NET_GetFileDescriptorID(env);
}

/*
* Class: java_net_SocketOutputStream
* Method: socketWrite
* Signature: (Ljava/io/FileDescriptor;[BII)V
*/
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
jobject fdObj, jbyteArray data,
jint off, jint len) {
char *bufP;
char BUF[MAX_BUFFER_LEN];
int buflen;
int fd;

if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
return;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (IS_NULL(data)) {
JNU_ThrowNullPointerException(env, "data argument");
return;
}

/*
* Use stack allocate buffer if possible. For large sizes we allocate
* an intermediate buffer from the heap (up to a maximum). If heap is
* unavailable just use our stack buffer.
*/
if (len <= MAX_BUFFER_LEN) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
} else {
buflen = min(MAX_HEAP_BUFFER_LEN, len);
bufP = (char *)malloc((size_t)buflen);
if (bufP == NULL) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
}
}

while(len > 0) {
int loff = 0;
int chunkLen = min(buflen, len);
int llen = chunkLen;
int retry = 0;

(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);

while(llen > 0) {
int n = send(fd, bufP + loff, llen, 0);
if (n > 0) {
llen -= n;
loff += n;
continue;
}

/*
* Due to a bug in Windows Sockets (observed on NT and Windows
* 2000) it may be necessary to retry the send. The issue is that
* on blocking sockets send/WSASend is supposed to block if there
* is insufficient buffer space available. If there are a large
* number of threads blocked on write due to congestion then it's
* possile to hit the NT/2000 bug whereby send returns WSAENOBUFS.
* The workaround we use is to retry the send. If we have a
* large buffer to send (>2k) then we retry with a maximum of
* 2k buffer. If we hit the issue with <=2k buffer then we backoff
* for 1 second and retry again. We repeat this up to a reasonable
* limit before bailing out and throwing an exception. In load
* conditions we've observed that the send will succeed after 2-3
* attempts but this depends on network buffers associated with
* other sockets draining.
*/
if (WSAGetLastError() == WSAENOBUFS) {
if (llen > MAX_BUFFER_LEN) {
buflen = MAX_BUFFER_LEN;
chunkLen = MAX_BUFFER_LEN;
llen = MAX_BUFFER_LEN;
continue;
}
if (retry >= 30) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"No buffer space available - exhausted attempts to queue buffer");
if (bufP != BUF) {
free(bufP);
}
return;
}
Sleep(1000);
retry++;
continue;
}

/*
* Send failed - can be caused by close or write error.
*/
if (WSAGetLastError() == WSAENOTSOCK) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
} else {
NET_ThrowCurrent(env, "socket write error");
}
if (bufP != BUF) {
free(bufP);
}
return;
}
len -= chunkLen;
off += chunkLen;
}

if (bufP != BUF) {
free(bufP);
}
}

【java源码系列】 - Object

架构

  • Object.java(rt.jar) 调用 jdk的native方法(Object.c)。
  • native方法 调用hotspot jvm的方法(jvm.cpp)。

Object.java

  • java source版本:oracle 1.7

Object 还有隐形的构造函数.

1
2
new Object();
new Object[int]; // 在ArrayList的构造函数中用到了

疑问:

为什么Object.java中看不到构造函数

编译器为它生成了构造函数

为什么在stack trace中看不到调用Object.

有7个native方法。

  • private static native void registerNatives();
  • protected native Object clone() throws CloneNotSupportedException;
  • public final native Class<?> getClass();
  • public native int hashCode();
  • public final native void notify();
  • public final native void notifyAll();
  • public final native void wait(long timeout) throws InterruptedException;
  • 但是构造函数是怎样实现的呢?(见jvm目录的init.md)

由于Object类中有JNI方法调用,按照JNI的规则,应当生成JNI 的头文件。
在此目录下执行javah -jni java.lang.Object 指令,将生成一个java_lang_Object.h头文件(自动生成的header,没什么意义)

Object.c

  • source版本:openjdk-7-fcs-src-b147-27_jun_2011
  • 路径: openjdk\jdk\src\share\native\java\lang\Object.c
  • 没有Object.h

Object.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
/*-
* Implementation of class Object
*
* former threadruntime.c, Sun Sep 22 12:09:39 1991
*/

#include <stdio.h>
#include <signal.h>
#include <limits.h>

#include "jni.h"
#include "jni_util.h"
#include "jvm.h"

#include "java_lang_Object.h"

// JVM_这些函数是在jvm.c中实现的
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode}, // 返回int
{"wait", "(J)V", (void *)&JVM_MonitorWait}, // 返回void,参数是long
{"notify", "()V", (void *)&JVM_MonitorNotify}, // 返回void
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, // 返回void
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, // 返回Ojbect
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}

其中JNINativeMethod的结构体如下:

1
2
3
4
5
typedef struct {
char *name; // Java中函数的名字
char *signature; // signature 方法签名,描述了函数的参数和返回值
void *fnPtr; // native实现的函数指针,指向C函数
} JNINativeMethod;

跟踪 hashcode

1
2
3
4
5
6
7
8
9
10
11
// jvm.h 路径: openjdk\hotspot\src\share\vm\prims\jvm.h
JNIEXPORT jint JNICALL
JVM_IHashCode(JNIEnv *env, jobject obj);


// jvm.cpp 路径: openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
// as implemented in the classic virtual machine; return 0 if object is NULL
return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END

FastHashCode才是真正计算hashcode的代码

FastHashCode

这是hashCode()的具体实现

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
// 路径: openjdk\hotspot\src\share\vm\runtime\synchronizer.cpp
intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
if (UseBiasedLocking) {
// NOTE: many places throughout the JVM do not expect a safepoint
// to be taken here, in particular most operations on perm gen
// objects. However, we only ever bias Java instances and all of
// the call sites of identity_hash that might revoke biases have
// been checked to make sure they can handle a safepoint. The
// added check of the bias pattern is to avoid useless calls to
// thread-local storage.
if (obj->mark()->has_bias_pattern()) {
// Box and unbox the raw reference just in case we cause a STW safepoint.
Handle hobj (Self, obj) ;
// Relaxing assertion for bug 6320749.
assert (Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(),
"biases should not be seen by VM thread here");
BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());
obj = hobj() ;
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
}

// hashCode() is a heap mutator ...
// Relaxing assertion for bug 6320749.
assert (Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(), "invariant") ;
assert (Universe::verify_in_progress() ||
Self->is_Java_thread() , "invariant") ;
assert (Universe::verify_in_progress() ||
((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant") ;

ObjectMonitor* monitor = NULL;
markOop temp, test;
intptr_t hash;
markOop mark = ReadStableMark (obj);

// object should remain ineligible for biased locking
assert (!mark->has_bias_pattern(), "invariant") ;

if (mark->is_neutral()) {
hash = mark->hash(); // this is a normal header 对象的hashcode存储在对象头里
if (hash) { // if it has hash, just return it 注意这里有个cache,对于同一个Ojbect,第一次调用Object.hashCode将会执行实际的计算并记入cache,以后直接从cache中取出。
return hash;
}
hash = get_next_hash(Self, obj); // allocate a new hash code
temp = mark->copy_set_hash(hash); // merge the hash code into header
// use (machine word version) atomic operation to install the hash
test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);
if (test == mark) {
return hash;
}
// If atomic operation failed, we must inflate the header
// into heavy weight monitor. We could add more code here
// for fast path, but it does not worth the complexity.
} else if (mark->has_monitor()) {
monitor = mark->monitor();
temp = monitor->header();
assert (temp->is_neutral(), "invariant") ;
hash = temp->hash();
if (hash) {
return hash;
}
// Skip to the following code to reduce code size
} else if (Self->is_lock_owned((address)mark->locker())) {
temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned
assert (temp->is_neutral(), "invariant") ;
hash = temp->hash(); // by current thread, check if the displaced
if (hash) { // header contains hash code
return hash;
}
// WARNING:
// The displaced header is strictly immutable.
// It can NOT be changed in ANY cases. So we have
// to inflate the header into heavyweight monitor
// even the current thread owns the lock. The reason
// is the BasicLock (stack slot) will be asynchronously
// read by other threads during the inflate() function.
// Any change to stack may not propagate to other threads
// correctly.
}

// Inflate the monitor to set hash code
monitor = ObjectSynchronizer::inflate(Self, obj);
// Load displaced header and check it has hash code
mark = monitor->header();
assert (mark->is_neutral(), "invariant") ;
hash = mark->hash(); // 取出缓存
if (hash == 0) {
hash = get_next_hash(Self, obj); // 实际计算
temp = mark->copy_set_hash(hash); // merge hash code into header
assert (temp->is_neutral(), "invariant") ;
test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark);
if (test != mark) {
// The only update to the header in the monitor (outside GC)
// is install the hash code. If someone add new usage of
// displaced header, please update this code
hash = test->hash();
assert (test->is_neutral(), "invariant") ;
assert (hash != 0, "Trivial unexpected object/monitor header usage.");
}
}
// We finally get the hash
return hash;
}

get_next_hash

这才是核心代码

又调用的get_next_hash()

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
// 路径: openjdk\hotspot\src\share\vm\runtime\synchronizer.cpp
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}

value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}

hashCode()并不是简单的返回内存地址。
OpenJDK一共实现了5中不同的计算hash值的方法,通过
这段代码中hashCode进行切换。其中hashCode == 4的是直接使用地址的(前面的实验说明OpenJDK默认情况下并没有使用这种方式,或许可以通过运行/编译时参数进行选择)。

###

结论

前面通过JNI验证已经能够得到很显然的结论,hashCode返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。

跟踪wait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
JNIEXPORT void JNICALL
JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);


// 路径: openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
JVMWrapper("JVM_MonitorWait");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");
JavaThreadInObjectWaitState jtiows(thread, ms != 0);
if (JvmtiExport::should_post_monitor_wait()) {
JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
}
ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END

跟踪notify

1
2
3
4
5
6
7
8
9
10
11
JNIEXPORT void JNICALL
JVM_MonitorNotify(JNIEnv *env, jobject obj);


// 路径: openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
JVMWrapper("JVM_MonitorNotify");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object");
ObjectSynchronizer::notify(obj, CHECK);
JVM_END

跟踪clone

1
2
3
4
5
6
7
8
9
10
JNIEXPORT jobject JNICALL
JVM_Clone(JNIEnv *env, jobject obj);


// 路径: openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;

跟踪 getClass

跟踪

参考

http://blog.csdn.net/xusiwei1236/article/details/45152201

byte code

  • 通过编译后的jar包(即class文件),查看byte code.
  • 运行 javap -c java.lang.Object > a.txt,得到以下的byte code

如果执行不成功,看看是否把jdk的lib加入到了classpath:

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
Compiled from "Object.java"
public class java.lang.Object {
public java.lang.Object(); // 什么都不干?不需要调用<init>吗?
Code:
0: return

public final native java.lang.Class<?> getClass();

public native int hashCode();

public boolean equals(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn

protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;

public java.lang.String toString();
Code:
0: new #1 // class java/lang/StringBuilder
3: dup
4: invokespecial #2 // Method java/lang/StringBuilder."<init>":()V // 注意这里,调用了<init>
7: aload_0
8: invokevirtual #3 // Method getClass:()Ljava/lang/Class;
11: invokevirtual #4 // Method java/lang/Class.getName:()Ljava/lang/String;
14: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: ldc #6 // String @
19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: aload_0
23: invokevirtual #7 // Method hashCode:()I
26: invokestatic #8 // Method java/lang/Integer.toHexString:(I)Ljava/lang/String;
29: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: areturn

public final native void notify();

public final native void notifyAll();

public final native void wait(long) throws java.lang.InterruptedException;

public final void wait(long, int) throws java.lang.InterruptedException;
Code:
0: lload_1
1: lconst_0
2: lcmp
3: ifge 16
6: new #10 // class java/lang/IllegalArgumentException
9: dup
10: ldc #11 // String timeout value is negative
12: invokespecial #12 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
15: athrow
16: iload_3
17: iflt 26
20: iload_3
21: ldc #13 // int 999999
23: if_icmple 36
26: new #10 // class java/lang/IllegalArgumentException
29: dup
30: ldc #14 // String nanosecond timeout value out of range
32: invokespecial #12 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
35: athrow
36: iload_3
37: ldc #15 // int 500000
39: if_icmpge 52
42: iload_3
43: ifeq 56
46: lload_1
47: lconst_0
48: lcmp
49: ifne 56
52: lload_1
53: lconst_1
54: ladd
55: lstore_1
56: aload_0
57: lload_1
58: invokevirtual #16 // Method wait:(J)V
61: return

public final void wait() throws java.lang.InterruptedException;
Code:
0: aload_0
1: lconst_0
2: invokevirtual #16 // Method wait:(J)V
5: return

protected void finalize() throws java.lang.Throwable;
Code:
0: return

static {};
Code:
0: invokestatic #17 // Method registerNatives:()V
3: return
}

C:\Program Files\java\jdk1.7.0_67\jre\lib\rt>

其他

  • object header: it’s JVM dependent, 具体参考JVM

扩展阅读

陈云霁报告 - 听课笔记

陈云霁

陈云霁 14岁上大学 24岁博士毕业 科大少年班。青年千人。

寒武纪

AI+芯片

寒武纪作为背靠中科院计算所和中科曙光的AI芯片独角兽公司,在芯片开发实力上处于国内领先地位。
目前1A芯片通过IP授权形式进入华为手机,并与中科曙光进行产业链互补。

  1. 2013 研发全球首个深度学习处理器架构DianNao,是智能芯片领域全球被引次数最多的论文
  2. 2014 研发全球首个多核深度学习处理器架构DaDianNao,是智能芯片全球被引次数第二的论文
  3. 2015 成功研制深度学习专用芯片寒武纪
  4. 2016 发布商用深度学习深度学习处理器IP产品 寒武纪1A,以及人工智能专用指令集Cambricon ISA
  5. 2017 寒武纪1A授权华为海思使用在Kiring 970手机芯片中
  6. 2017 发布低功耗场景视觉应用处理器1H8,通用性更高的1H16和智能驾驶新片1M
  7. 2018 发布针对服务器推理和训练的机器学习处理器 MLU100

从2017年起获得了中科院为期18个月的共计1000万元的专项资金支持。

目前寒武纪主要有三条产品线:

  1. IP授权,智能IP指令集可授权集成到手机、安防、可穿戴设备等终端芯片中,2016年全年拿到1亿元订单
  2. 在智能云服务器芯片领域,作为PCIe加速卡插在云服务器上,希望能布局进入人工智能训练和推理市场
  3. 开发面向家用智能服务机器人、智能驾驶、智能安防等领域的应用芯片

深度学习处理器

寒武纪做的是终端芯片?还是服务器芯片?

貌似

智能计算和普通计算的区别是什么?

符号主义不再流行

符号逻辑表示问题,求解逻辑表达式

行为主义

联结主义 - 人工神经网络

把神经细胞抽象成数字,把突出抽象成数字。

  • 轴突 - 输出
  • 树突 - 输入
  • 交接地方叫 -

人工神经网络一千亿个突触,人脑有百万亿突触。数量级的差距。

人工神经元与生物神经元细胞的区别。

逐层抽象处理

##

现有硬件的缺陷,cpu gpu高能耗,低性能。alpha go 几千台。耗电几千瓦,李世乭只需要吃碗饭。

ai算法不错,落地困难,有硬件原因。cpu/gpu构建大规模神经网络,消耗很大。因此,

华为,阿里,曙光等手机都集成了寒武纪芯片。

  • 拍照时识别东西。自动调节光圈,
  • 本地机器翻译,不需要联网。牛逼,模型多大啊?很消耗内存吧。本地实时翻译。

存在的问题

传统的ASIC(将给定算法硬件化)的思路无法解决深度学习处理的需求。(并不难,比如把C的程序编程virlog程序)

  • 有限规模的硬件 VS 任意规模的算法
    • 电路做神经元 突触。芯片流片后都是固定的,多少神经元。
    • 寒武纪采用的思路是:硬件神经元的虚拟化。通过时分复用,把有限规模的硬件虚拟成任意大规模的硬件
      • 缺陷是,数据搬运
  • 结构固定的硬件 VS 千变万化的算法
    • 任务不同(下棋,语音,图像),结构差异(卷积、全连接等)。每天有大量新算法 VS 芯片研发周期长
    • 寒武纪解决方案:抽象各种网络的通用算子,找不同算法,最耗时的部分。
      • 主要运算:向量内积、向量距离、计数、非线性函数、排序
      • 三个柱子:所有变量可概括为三类。
      • 新算法来了,我们只需要对现有算子的组合
  • 能耗受限的硬件 VS 精度优先的算法
    • 手机上不要超过1w,不然手机发烫,电池续航也不行
    • 服务器不超过300w,散热问题
    • google大脑不考虑能耗,只考虑精度
    • 做硬件和做算法的人就存在矛盾
    • 寒武纪解决方案:稀疏神经网络处理器
      • 很好。跳过90%的神经元。问题是多小能够稀疏化,比如0.01导致用户体验下降,那么就调高点。通过运行时,动态调节稀疏度。

华为mate10的深度学习处理器,比iphone10高效。牛逼呀

google大脑采用了1.6万个cpu核,如果提高1万倍,就可以融入手机。寒武纪目标提高性能1万倍。

寒武纪

  • 是通用机器学习处理器,大部分机器学习算法都支持。 牛逼

7nm,流片成本1亿人民币。没有百万量就不能回本。

  • 距离通用的gpu,tpu距离多远?往电脑上一插就能用的。

应用面没gpu广,比如gpu能处理图片,寒武纪不擅长图片。

性能功耗比: 寒武纪 > tpu > gpu

tpu做cnn效率高,做lstm效率低。

云端PCA加速卡,曙光出了单机20卡,基于寒武纪。

寒武纪芯片是否支持caffe tf pytorch?支持

陈天霁:华为芯片最领先,展讯、比特大陆(挖矿芯片,异军突起)

#

Demo: No-Title

This is a post, which has no title.

Typically, we can locate this file by URL with default hexo settings.

1
permalink: :year/:month/:day/:title/

e.g. URL is as follows:

1
https://blog.eson.org/2000/01/01/demo/hexo/no-title/

It shows the full path of this post _post/demo/hexo/no-title.md.

Trouble in hexo-abbrlink

hexo-abbrlink is a great plugin for hexo users. I setup hexo-abbrlink in my post.

In this case, url becomes

1
https://blog.eson.org/pub/0/

God, I want to locate this post and add a title. But, how can I locate my error-post?

Solution

It would be better if hexo-abbrlink log the post with no title. Give me a warning.

with hexo-auto-category

In this post, you may notice that the category is demo, hexo.
Actually, the full path of this post is _post/demo/hexo/no-title.md. It really helps.

hexo-auto-category binds folder structure to category. It is also a good way to locate your post.

Summary

hexo-abbrlink and hexo-auto-category