{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/2020-05-31-jvm/","result":{"data":{"markdownRemark":{"id":"5e0a022a-c12b-5154-816a-77545536cc67","html":"<p><img src=\"/img/chemex.jpg\" alt=\"chemex\">  </p>\n<p>JVM  </p>\n<blockquote>\n<p>并发原理</p>\n</blockquote>\n<h2>POSIX Thread</h2>\n<h3></h3>\n<h3>基础 API</h3>\n<h4>jvm C 语言创建线程</h4>\n<p>os_linux.cpp</p>\n<p>pthread_create</p>\n<h4>java 创建线程</h4>\n<blockquote>\n<p>Java 中创建线程只有 new Thread(Runable) 一种方式</p>\n</blockquote>\n<pre><code class=\"language-java\">public class ThreadHelloWorld {\n    public static void main(String[] args) throws Exception{\n        // Java 线程和 JVM OS 线程不是同一个对象\n        // pthread_create()\n        Thread t1 = new Thread(ThreadHelloWorld::helloWorld);\n        t1.start();// pthread_create()\n        t1.join();// pthread_join()\n        // java 线程 isAlive() == false 时， JVM 线程已经消亡（C++: delete this）\n        System.out.prinf(\"线程状态： %s，线程是否存活：%s\", t1.getState(), t1.isAlive());\n    }\n    private static void helloWorld() {\n        System.out.printf(\"Thread[id; %d] - Hello World!\", Thread.currentTread().getId());\n    }\n}\n</code></pre>\n<h3>同步 API</h3>\n<h4>互斥（Mutex）</h4>\n<h5>Lock</h5>\n<pre><code class=\"language-java\"></code></pre>\n<h2>Java 内存模型(Java Memory Model - JMM)</h2>\n<p>since jdk 1.5\nDoug Lea</p>\n<h3>共享变量(Shared Variables)</h3>\n<p>Java 普通对象存放在 Heap 中。\n对于一个对象来说他的地址是确定的，但是对象中的属性地址是计算出来的。</p>\n<p>重排序\nJava 中 String 比较特殊\nJava 不是完全的面向对象语言， Object + 基本类型 + 数组</p>\n<p>object 地址 + offsets\nJava 针对对象属性采用相对地址计算真实地址，在 cpu 寄存器做了 cas(compare and set) 操作。</p>\n<p>Local variables 线程间是隔离的。 </p>\n<h3>动作(Actionss)</h3>\n<p>同步区别在于范围</p>\n<h3>顺序(Order)</h3>\n<h4>程序执行顺序</h4>\n<ol>\n<li>顺序执行</li>\n</ol>\n<p>单线程是没有数据竟争的，所有程序会顺序执行。\n数据竞争应该是可以被 jvm 识别的。</p>\n<p>hashmap 读的时候是线程安全的。</p>\n<ol start=\"2\">\n<li>同步顺序</li>\n</ol>\n<p>保证程序串行 </p>\n<p>valotile 保证可见性,写的时候加锁，读的时候 valotile</p>\n<ol start=\"3\">\n<li>Thread.start 方法在 Thread.run 方法之前执行</li>\n</ol>\n<blockquote>\n<p>Thread.start method 有 sychronized 关键字。</p>\n</blockquote>\n<ol start=\"4\">\n<li>x</li>\n</ol>\n<p>final 线程在对象创建之前，不会读到字段在初始化的中间状态。\n对象初始化是安全的</p>\n<ol start=\"5\">\n<li>x</li>\n<li>xx</li>\n</ol>\n<p>interrupt 修改了一个状态，保证该状态其他线程可见</p>\n<p>Feture 通过判断线程的 interrupted 状态来控制业务流程</p>\n<h4>happens of before</h4>\n<p>这个跟最终一致性不是同一个思想吗</p>\n<p>顺序不重要</p>\n<ol>\n<li>同一线程，程序顺序</li>\n<li>\n<p>构造早于销毁之前</p>\n<ol>\n<li>构造对象是在用户线程（main/子线程）</li>\n<li>Finalizer 操作是 JVM 线程(GC)</li>\n<li>对象的 Heap 中， Heap 对于线程是共享的</li>\n</ol>\n</li>\n<li>\n<p>同步关系>= heapens-before</p>\n<ol>\n<li>只关心前后关系，不关心是否串行</li>\n<li>ConcurrentSkipListMap</li>\n</ol>\n</li>\n<li>传递性 hb(x,y) hb(y,z) -> hb(x,z)</li>\n</ol>\n<p>happens-before</p>\n<p>lock -> object.wait -> unlock</p>\n<p>thread.join() 语义</p>\n<pre><code class=\"language-java\">private static void threadStartAndJoin() throws InterruptedException {\n    Thread t = new Thread(() -> {\n        // action\n    });\n    // main 线程调用线程 t 的 join() 方法\n    // 在 join() 方法返回之前， t 所有的 actions 已执行结束\n    t.join();\n}\n</code></pre>\n<ul>\n<li>unlock happens-before 其他 lock</li>\n<li>volatile 字段写 happens-before 其他 subsequent 的读</li>\n<li>thread.start() happends-before 此线程其他 action</li>\n<li>当前线程中的子线程的 actions happens-before thread.join()</li>\n<li>兑现的默认初始化操作 happens-before 其他 actions(除了默认写)</li>\n</ul>\n<h3>延伸</h3>\n<p>c 语言来保证\nvolatile</p>\n<p>保证顺序，并不易定安全。不同作用域？\n最终一致性</p>\n<p>Connection</p>\n<p>TRANSACTION<em>READ</em>COMMITTED,\n结构性不同，单机 多机器</p>\n<p>account = 5</p>\n<p>t1 add 5</p>\n<p>t2 minus 2</p>\n<p>t3 minus 3</p>\n<p>t3 -> t1 -> t2\n5 - 3 = 2\n2 + 5 = 7\n7 - 2 = 5\n可见性，最终一致性。\n假如不可见， t3 account = 5 - 3 = 2, t2 account = 5 - 2 = 3\n每次读都是从内存中读的，不是 cpu 中的</p>\n<p>锁，串行，\n类似 TRANSACTION_SERIALIZEABLE</p>\n<p>执行按照定义的顺序</p>\n<p>5 + 5 - 2 - 3 = 5</p>\n<p>CAS\nAtomic</p>\n<h4>内存屏障(Memory Barriers)</h4>\n<p>jvm orderAccess.hpp </p>\n<h3>java 几大疑问</h3>\n<ol>\n<li>synchronized</li>\n<li>\n<p>volatile</p>\n<ol>\n<li>限制重排</li>\n</ol>\n</li>\n<li>final and immutability</li>\n<li>native</li>\n<li>actomic </li>\n</ol>\n<h2>Java 并发框架（J.U.C）AQS 原理</h2>\n<h3>AbstratQueuedSynchronizer（AQS）原理</h3>\n<p>Provides a framework for implementing blocking locks and related\nsynchronizers (semaphores, events, etc) that rely on\nfirst-in-first-out (FIFO) wait queues.</p>\n<p>java.util.concurrent.locks.AbstractQueuedSynchronizer</p>\n<p>双向队列</p>\n<blockquote>\n<p>java debugger: 会锁定所有线程</p>\n</blockquote>\n<p>头线程只有状态</p>\n<p>公平锁：严格的 FIFO\n非公平锁：\n线程池中新入队的线程会竞争锁，所以在 acquireQueued 时再次 tryAcquire\n//PS: LockSupport.unpark()  解锁</p>\n<p>抛出 InterruptedException 时 interrupted 状态被清除</p>\n<h3>条件变量（ConditionObject）原理</h3>\n<h3>SUMMARY</h3>\n<h2>JVM 并发实现</h2>\n<h3>java.lang.Thread API</h3>\n<h4>基本操作</h4>\n<h5>创建 - new Thread()</h5>\n<p>new Thread() 创建一个普通的 Java 对象，不涉及线程启动</p>\n<h5>启动 - thread.start()</h5>\n<p>thread.start() 引导线程启动，不一定马上启动（非阻塞），最终会调用 thread.run 方法</p>\n<pre><code class=\"language-java\">Thread.java\n\n// 注册 Native 方法\n/* Make sure registerNatives is the first thing &#x3C;clinit> does. */\nprivate static native void registerNatives();\nstatic {\n    registerNatives();\n}\n\n/**\n * 线程安全\n */\npublic synchronized void start() {\n    ...\n    group.add(this); // 将当前线程添加到所在 ThreadGroup\n    boolean started = false;\n    try {\n        start0(); // Native 方法\n        started = true;\n    } finally {\n        try {\n            if (!started) {\n                group.threadStartFailed(this);\n            }\n        } catch (Throwable ignore) {\n            /* do nothing. If start0 threw a Throwable then\n                it will be passed up the call stack */\n        }\n    }\n}\n\nprivate native void start0(); // JNI Java 调用 JVM 方法\n</code></pre>\n<p>类名： java.lang.Thread\n方法名： java.lang.Thread#start0()\nJNI（C函数）: JVM_StartThread</p>\n<pre><code class=\"language-c\">JNIEXPORT void JNICALL\nJava_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)\n{\n    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));\n}\n</code></pre>\n<p>Thread.c 方法映射</p>\n<pre><code class=\"language-c\">static JNINativeMethod methods[] = {\n    {\"start0\",           \"()V\",        (vo id *)&#x26;JVM_StartThread},\n    {\"stop0\",            \"(\" OBJ \")V\", (void *)&#x26;JVM_StopThread},\n    {\"isAlive\",          \"()Z\",        (void *)&#x26;JVM_IsThreadAlive},\n    {\"suspend0\",         \"()V\",        (void *)&#x26;JVM_SuspendThread},\n    {\"resume0\",          \"()V\",        (void *)&#x26;JVM_ResumeThread},\n    {\"setPriority0\",     \"(I)V\",       (void *)&#x26;JVM_SetThreadPriority},\n    {\"yield\",            \"()V\",        (void *)&#x26;JVM_Yield},\n    {\"sleep\",            \"(J)V\",       (void *)&#x26;JVM_Sleep},\n    {\"currentThread\",    \"()\" THD,     (void *)&#x26;JVM_CurrentThread},\n    {\"interrupt0\",       \"()V\",        (void *)&#x26;JVM_Interrupt},\n    {\"holdsLock\",        \"(\" OBJ \")Z\", (void *)&#x26;JVM_HoldsLock},\n    {\"getThreads\",        \"()[\" THD,   (void *)&#x26;JVM_GetAllThreads},\n    {\"dumpThreads\",      \"([\" THD \")[[\" STE, (void *)&#x26;JVM_DumpThreads},\n    {\"setNativeName\",    \"(\" STR \")V\", (void *)&#x26;JVM_SetNativeThreadName},\n};\n</code></pre>\n<p>stackSize 修改方式</p>\n<ol>\n<li>Xss 设置 VM 全局配置</li>\n<li>Thread 针对性设置</li>\n</ol>\n<pre><code class=\"language-java\"> public Thread(ThreadGroup group, Runnable target, String name,\n                  long stackSize) {\n    init(group, target, name, stackSize);\n}\n</code></pre>\n<p>方法签名： java.lang.Thread#start0()\nNative 方法： JVM_StartThread\n实现入口：</p>\n<pre><code class=\"language-C++\">JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))\n...\n  {\n    ...\n\n    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent\n    // re-starting an already started thread, so we should usually find\n    // that the JavaThread is null. However for a JNI attached thread\n    // there is a small window between the Thread object being created\n    // (with its JavaThread set) and the update to its threadStatus, so we\n    // have to check for this\n    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {\n      throw_illegal_thread_state = true;\n    } else {\n        jlong size =\n             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));\n      NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;)\n      size_t sz = size > 0 ? (size_t) size : 0;\n      native_thread = new JavaThread(&#x26;thread_entry, sz);\n        ...\n    }\n  }\n</code></pre>\n<p><code>native_thread</code> 创建了 JavaThread 类型对象\n&#x26;thread<em>entry 即函数指针引用函数 thread</em>entry:</p>\n<pre><code class=\"language-C++\">static void thread_entry(JavaThread* thread, TRAPS) {\n  HandleMark hm(THREAD);\n  Handle obj(THREAD, thread->threadObj()); // 1 thread->threadObj() 返回 java 代码创建 java.lang.Thread 对象\n  JavaValue result(T_VOID);\n  JavaCalls::call_virtual(&#x26;result,\n                          obj,             \n                          SystemDictionary::Thread_klass(), \n                          vmSymbols::run_method_name(), // 2 java.lang.Thread#run 方法名称\n                          vmSymbols::void_method_signature(), // void 方法签名\n                          THREAD);\n}\n</code></pre>\n<p>通过 thread_entry 方法查找 vmSymbols.hpp 模版查找到 Thread 对象的 run() 方法</p>\n<p>JavaThread::JavaThread 构造函数</p>\n<pre><code class=\"language-C++\">JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :\n                       Thread() {\n    ...\n    initialize();\n  _jni_attach_state = _not_attaching_via_jni;\n  set_entry_point(entry_point); // 设置 thread_entry 到当前对象\n  // Create the native thread itself.\n  // %note runtime_23\n  os::ThreadType thr_type = os::java_thread;\n  thr_type = entry_point == &#x26;compiler_thread_entry ? os::compiler_thread : os::java_thread;\n  os::create_thread(this, thr_type, stack_sz); // 创建操作系统线程\n}\n</code></pre>\n<p>第一个构造参数 entry<em>point 实际指 thread</em>entry</p>\n<p>os::create<em>thread linux 实现（os</em>linux.cpp）</p>\n<pre><code class=\"language-C\">bool os::create_thread(Thread* thread, ThreadType thr_type,\n                       size_t req_stack_size) {\n ...\n  // Allocate the OSThread object\n  OSThread* osthread = new OSThread(NULL, NULL);\n  if (osthread == NULL) {\n    return false;\n  }\n\n ...\n\n  // init thread attributes\n  pthread_attr_t attr;\n  pthread_attr_init(&#x26;attr);\n  pthread_attr_setdetachstate(&#x26;attr, PTHREAD_CREATE_DETACHED);\n\n  // Calculate stack size if it's not specified by caller.\n  size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);\n  // In glibc versions prior to 2.7 the guard size mechanism\n  // is not implemented properly. The posix standard requires adding\n  // the size of the guard pages to the stack size, instead Linux\n  // takes the space out of 'stacksize'. Thus we adapt the requested\n  // stack_size by the size of the guard pages to mimick proper\n  // behaviour. However, be careful not to end up with a size\n  // of zero due to overflow. Don't add the guard page in that case.\n  size_t guard_size = os::Linux::default_guard_size(thr_type);\n  // Configure glibc guard page. Must happen before calling\n  // get_static_tls_area_size(), which uses the guard_size.\n  pthread_attr_setguardsize(&#x26;attr, guard_size);\n\n  size_t stack_adjust_size = 0;\n  if (AdjustStackSizeForTLS) {\n    // Adjust the stack_size for on-stack TLS - see get_static_tls_area_size().\n    stack_adjust_size += get_static_tls_area_size(&#x26;attr);\n  } else {\n    stack_adjust_size += guard_size;\n  }\n\n  stack_adjust_size = align_up(stack_adjust_size, os::vm_page_size());\n  if (stack_size &#x3C;= SIZE_MAX - stack_adjust_size) {\n    stack_size += stack_adjust_size;\n  }\n  assert(is_aligned(stack_size, os::vm_page_size()), \"stack_size not aligned\");\n\n  int status = pthread_attr_setstacksize(&#x26;attr, stack_size);\n  assert_status(status == 0, status, \"pthread_attr_setstacksize\");\n\n  ThreadState state;\n\n  {\n    pthread_t tid;\n    int ret = pthread_create(&#x26;tid, &#x26;attr, (void* (*)(void*)) thread_native_entry, thread);\n\n    char buf[64];\n    if (ret == 0) {\n      log_info(os, thread)(\"Thread started (pthread id: \" UINTX_FORMAT \", attributes: %s). \",\n        (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), &#x26;attr));\n    } else {\n      log_warning(os, thread)(\"Failed to start thread - pthread_create failed (%s) for attributes: %s.\",\n        os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), &#x26;attr));\n      // Log some OS information which might explain why creating the thread failed.\n      log_info(os, thread)(\"Number of threads approx. running in the VM: %d\", Threads::number_of_threads());\n      LogStream st(Log(os, thread)::info());\n      os::Posix::print_rlimit_info(&#x26;st);\n      os::print_memory_info(&#x26;st);\n      os::Linux::print_proc_sys_info(&#x26;st);\n      os::Linux::print_container_info(&#x26;st);\n    }\n    ...\n    pthread_attr_destroy(&#x26;attr);\n    ...\n  return true;\n}\n</code></pre>\n<p>thread<em>native</em>entry thread->call_run() 方法执行 java.lang.Thread#run()</p>\n<ol>\n<li>java 调用 native 方法 start0()</li>\n<li>start0 映射到 jvm 中的 JVM<em>StartThread(&#x26;thread</em>entry, sz)</li>\n<li>JVM_StartThread 中 new JavaThread 对象，第一个构造参数映射 java 对象相关信息</li>\n<li>JavaThread 构造函数会调用 os 的 creat_thread 方法</li>\n<li>creat<em>thread 调用 POSIX Thread `pthread</em>create`</li>\n<li>pthread_create 执行 run 回调函数</li>\n<li>java.lang.Thread#start() 方法调用完成,JVM 所创建的 JavaThread 对象就移除（delete this）</li>\n</ol>\n<p>当 java.lang.Thread#start() 方法调用完成， JVM 所创建的 JavaThread 对象就移除（delete this）。此时 java.lang.Thread 对象还没有回收。</p>\n<p>自旋锁(spinlocks)\nthread.cpp SpinAcquire\nCLH 队列</p>\n<ul>\n<li>原生 - JVM ParkEvent（自旋）</li>\n<li>\n<p>变种 - JDK AQS Node（阻塞）</p>\n<ul>\n<li>LockSupport#park()</li>\n</ul>\n</li>\n</ul>\n<h5>join</h5>\n<p>调用 Object 的 native 方法 wait:</p>\n<pre><code class=\"language-java\">public class Object {\n    ...\n    public final native void wait(long timeout) throws InterruptedException;\n    ...\n}\n</code></pre>\n<p>代码位置（jdk8）：\nObject.c (java.lang.Object 中的 native 方法都在这里做了映射) native 方法映射 wait - JVM<em>MonitorWait\njvm.cpp JVM</em>ENTRY(void, JVM_MonitorWait)</p>\n<pre><code class=\"language-C++\">JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))\n  JVMWrapper(\"JVM_MonitorWait\");\n  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));\n  JavaThreadInObjectWaitState jtiows(thread, ms != 0);\n  if (JvmtiExport::should_post_monitor_wait()) {\n    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);\n\n    // The current thread already owns the monitor and it has not yet\n    // been added to the wait queue so the current thread cannot be\n    // made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT\n    // event handler cannot accidentally consume an unpark() meant for\n    // the ParkEvent associated with this ObjectMonitor.\n  }\n  ObjectSynchronizer::wait(obj, ms, CHECK);\nJVM_END\n</code></pre>\n<p>ObjectSynchronizer::wait(obj, ms, CHECK); 会创建膨胀锁</p>\n<p>CLH 自旋\nAQS.Node CLH 变体双向队列节点阻塞]</p>\n<p>JVM parkEvent->park 及 jdk LockSupport.park(this) 底层同为 <code>pthread_cond_wait</code> 函数</p>\n<h3>volatile 原理</h3>\n<p>volatile 是一个 c++ 语意。</p>\n<h2>垃圾回收</h2>\n<h2>线程</h2>\n<h2>I/O</h2>\n<h3>jdk11</h3>\n<h2>quote</h2>\n<ul>\n<li><a href=\"https://jcp.org/en/jsr/overview\">JSR(Java Language Specification)</a></li>\n<li>JAVA 并发之美</li>\n</ul>\n<h2>TODO</h2>\n<p>CLH\nUnsafe\nOS\nwait\npark\nyield\nnotify</p>\n<ul>\n<li>JMM</li>\n<li>thread 原理</li>\n<li></li>\n</ul>","frontmatter":{"date":"May 31, 2020","title":"JVM","description":"JVM","tags":["JAVA","JVM","jconsole"]}}},"pageContext":{"id":"5e0a022a-c12b-5154-816a-77545536cc67"}}}