`

【深入Java开发】JVM源码分析之一个Java进程究竟能创建多少线程

阅读更多
概述
虽然这篇文章的标题打着JVM源码分析的旗号,不过本文不仅仅从JVM源码角度来分析,更多的来自于Linux Kernel的源码分析,今天要说的是JVM里比较常见的一个问题

这个问题可能有几种表述

一个Java进程到底能创建多少线程?
到底有哪些因素决定了能创建多少线程?
java.lang.OutOfMemoryError: unable to create new native thread的异常究竟是怎么回事
不过我这里先声明下可能不能完全百分百将各种因素都理出来,因为毕竟我不是做Linux Kernel开发的,还有不少细节没有注意到的,我将我能分析到的因素和大家分享一下,如果大家在平时工作中还碰到别的因素,欢迎在文章下面留言,让更多人参与进来讨论

从JVM说起
线程大家都熟悉,new Thread().start()即会创建一个线程,这里我首先指出一点new Thread()其实并不会创建一个真正的线程,只有在调用了start方法之后才会创建一个线程,这个大家分析下Java代码就知道了,Thread的构造函数是纯Java代码,start方法会调到一个native方法start0里,而start0其实就是JVM_StartThread这个方法

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))

  ...

      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

  ...  

  if (native_thread->osthread() == NULL) {
    ...
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  Thread::start(native_thread);

JVM_END
从上面代码里首先要大家关注下最后的那个if判断if (native_thread->osthread() == NULL),如果osthread为空,那将会抛出大家比较熟悉的unable to create new native thread OOM异常,因此osthread为空非常关键,后面会看到什么情况下osthread会为空

另外大家应该注意到了native_thread = new JavaThread(&thread_entry, sz),在这里才会真正创建一个线程

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
#ifndef SERIALGC
  , _satb_mark_queue(&_satb_mark_queue_set),
  _dirty_card_queue(&_dirty_card_queue_set)
#endif // !SERIALGC
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p", this);
  }
  initialize();
  _jni_attach_state = _not_attaching_via_jni;
  set_entry_point(entry_point);
  // Create the native thread itself.
  // %note runtime_23
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread;
  os::create_thread(this, thr_type, stack_sz);

}
上面代码里的os::create_thread(this, thr_type, stack_sz)会通过pthread_create来创建线程,而Linux下对应的实现如下:

bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
  assert(thread->osthread() == NULL, "caller responsible");

  // Allocate the OSThread object
  OSThread* osthread = new OSThread(NULL, NULL);
  if (osthread == NULL) {
    return false;
  }

  // set the correct thread state
  osthread->set_thread_type(thr_type);

  // Initial state is ALLOCATED but not INITIALIZED
  osthread->set_state(ALLOCATED);

  thread->set_osthread(osthread);

  // init thread attributes
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  // stack size
  if (os::Linux::supports_variable_stack_size()) {
    // calculate stack size if it's not specified by caller
    if (stack_size == 0) {
      stack_size = os::Linux::default_stack_size(thr_type);

      switch (thr_type) {
      case os::java_thread:
        // Java threads use ThreadStackSize which default value can be
        // changed with the flag -Xss
        assert (JavaThread::stack_size_at_create() > 0, "this should be set");
        stack_size = JavaThread::stack_size_at_create();
        break;
      case os::compiler_thread:
        if (CompilerThreadStackSize > 0) {
          stack_size = (size_t)(CompilerThreadStackSize * K);
          break;
        } // else fall through:
          // use VMThreadStackSize if CompilerThreadStackSize is not defined
      case os::vm_thread:
      case os::pgc_thread:
      case os::cgc_thread:
      case os::watcher_thread:
        if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
        break;
      }
    }

    stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
    pthread_attr_setstacksize(&attr, stack_size);
  } else {
    // let pthread_create() pick the default value.
  }

  // glibc guard page
  pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));

  ThreadState state;

  {
    // Serialize thread creation if we are running with fixed stack LinuxThreads
    bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
    if (lock) {
      os::Linux::createThread_lock()->lock_without_safepoint_check();
    }

    pthread_t tid;
    int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

    pthread_attr_destroy(&attr);

    if (ret != 0) {
      if (PrintMiscellaneous && (Verbose || WizardMode)) {
        perror("pthread_create()");
      }
      // Need to clean up stuff we've allocated so far
      thread->set_osthread(NULL);
      delete osthread;
      if (lock) os::Linux::createThread_lock()->unlock();
      return false;
    }

    // Store pthread info into the OSThread
    osthread->set_pthread_id(tid);
     ...
  }
   ...
  return true;
}
如果在new OSThread的过程中就失败了,那显然osthread为NULL,那再回到上面第一段代码,此时会抛出java.lang.OutOfMemoryError: unable to create new native thread的异常,而什么情况下new OSThread会失败,比如说内存不够了,而这里的内存其实是C Heap,而非Java Heap,由此可见从JVM的角度来说,影响线程创建的因素包括了Xmx,MaxPermSize,MaxDirectMemorySize,ReservedCodeCacheSize等,因为这些参数会影响剩余的内存
阅读全文直接点击:http://click.aliyun.com/m/9556/
分享到:
评论

相关推荐

    Java多线程编程全部源码

    这个资源包含的"SimpleThread"源码很可能是对Java基础线程操作的一个实例演示。下面将详细解释多线程的基本概念、创建方法以及相关的知识点。 1. **线程的概念**:线程是操作系统分配CPU时间的基本单位,一个进程...

    实战JAVA虚拟机 JVM故障诊断与性能优化

    JDK自带了一系列强大的诊断工具,如jps(Java进程查看器)、jstat(统计信息工具)、jinfo(配置信息工具)、jmap(内存映射工具)、jhat(堆转储分析工具)和jstack(线程堆栈跟踪工具)。熟练掌握这些工具的使用,...

    java多线程

    - JVM启动后会创建一个`java.exe`进程,并在这个进程中创建至少两个线程:主线程和垃圾回收线程。 - **主线程**:执行应用程序中的`main`函数中的代码。 - **垃圾回收线程**:负责清理主线程运行过程中在堆内存中...

    Java并发/多线程

    在Java中,每个Java虚拟机(JVM)实例都有一个主线程,可以通过创建额外的线程来执行并发任务。 2. **线程的创建** - Java提供了多种创建线程的方式,如通过实现Runnable接口或继承Thread类。推荐使用实现...

    嵌入式java虚拟机源码包括unix,winCE,windows 后端 - C.zip

    在计算机科学领域,Java虚拟机(JVM)是Java语言的核心组成部分,它为Java程序提供了一个跨平台的运行环境。当涉及到嵌入式系统时,这种特性变得更加重要,因为这些系统通常具有资源有限的特性。本文将探讨嵌入式...

    基于java的开发源码-远程文件传输工具 MammothCopy.zip

    本文将对名为MammothCopy的Java开发源码进行深入探讨,旨在揭示其工作原理和关键知识点。 一、Java远程文件传输基础 1. Java的Socket编程:MammothCopy的核心技术之一就是利用Java的Socket编程实现客户端与服务器...

    Java进程cpu占用率高

    综上所述,解决“Java进程CPU占用率高”问题需要从多个角度出发,包括但不限于代码优化、JVM调优、垃圾收集策略调整、监控与诊断工具的使用等。通过系统性地分析和改进,可以有效地降低Java进程对CPU的占用,提高...

    Java 线程

    本文将深入探讨Java线程相关的知识,包括线程的创建、线程同步和通信、线程状态以及如何利用工具进行线程分析。 1. **线程的创建** Java提供了两种创建线程的方式: - 通过实现`Runnable`接口。创建一个类实现`...

    JAVA上百实例源码以及开源项目源代码

    JAVA源码,媒体网络,山寨QQ,Java聊天程序 Java编写的山寨QQ,多人聊天+用户在线,程序分服务端和客户端,典型C/S结构, 当用户发送第一次请求的时候,验证用户登录,创建一个该qq号和服务器端保持通讯连接得线程,...

    java开源包8

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    基于Java的源码-lobby(经典board游戏 Domination).zip

    "基于Java的源码-lobby(经典board游戏 Domination)" 提供的是开发一个名为“lobby”的游戏的源代码,这款游戏是基于经典的board游戏“Domination”。 【board游戏】 Board游戏是指在二维平面上进行的策略游戏,...

    jvm-profiler,jvm分析器向kafka、控制台输出或自定义报告器发送度量.zip

    最后,我们提到"jvm-profiler-master"是一个源码包,意味着你可以深入研究其内部实现,学习如何构建这样的监控工具,或者根据自身需求进行二次开发。开源项目的特性也使得这个工具拥有良好的社区支持,遇到问题时...

    基于Java的实例源码-iqq(开源 QQ 工具 iQQ).zip

    在源码分析过程中,还会涉及到Java的多线程技术,以实现异步操作,如接收消息、更新界面等。Java提供了Thread类和Runnable接口来创建和管理线程,同时ExecutorService和Future接口提供了更高级的线程池管理。 此外...

    【转】Java内存泄露_JVM监控工具介绍jstack_jconsole_jinfo_jmap_jdb_jstat

    其次,`jconsole`是一个图形化的JVM监视工具,它可以提供内存使用情况、线程信息、类加载情况等实时数据。开发者可以通过jconsole连接到本地或远程的Java进程,直观地看到各种性能指标,并进行诊断。此外,jconsole...

    Java并发编程的艺术+源码

    Java通过`Thread`类提供了线程的创建和管理,同时JVM还提供了一些内置线程,如垃圾收集线程。 其次,书中会介绍Java并发工具类,包括`ExecutorService`、`Future`、`Callable`、`Semaphore`、`CyclicBarrier`、`...

    CRM+JAVA+客户关系管理分享源码学习a

    通过深入研究这个源码,学习者可以了解到Java在构建CRM系统时的具体应用,如数据库连接、MVC架构的应用、事务管理、多线程处理等,这对于提升Java开发者在企业级应用开发中的技能是非常有价值的。同时,也能理解和...

    jvisualvm来JVM监控

    它能够实时显示JVM的运行信息,包括内存使用情况、线程状态、类加载、垃圾收集、CPU使用率等,为开发者提供了一个直观的界面,方便快速定位问题。 二、jvisualvm的主要功能 1. **内存监控**:jvisualvm可以展示堆...

    Java线程学习和总结

    - **jstack工具**:用于查看Java进程的线程堆栈信息,帮助分析线程状态。 以上是Java线程学习的一些核心知识点,理解和掌握它们对于进行高效并发编程至关重要。在实际开发中,还需要结合具体场景灵活运用,确保...

    JVM内存区域划分Java系列2021.pdf

    Java虚拟机栈也是线程私有的,它描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存储了方法中局部变量,...

    Java源码获取程序运行环境的信息

    6. **Java Management API (JMAPI)**:这是一个标准的API,允许开发者创建和注册管理 beans 来获取系统级别的信息,如网络连接、磁盘空间、进程信息等。 7. **Java诊断工具**:如JDK自带的`jinfo`、`jmap`、`jstack...

Global site tag (gtag.js) - Google Analytics