Skip to content

1. FAQ

1.1 一个进程可以创建多少个线程

两个因素会影响可创建的线程上限:

  • 进程的虚拟内存空间上限,因为创建一个线程,操作系统需要为其分配一个栈空间,如果线程数量越多,所需的栈空间就要越大,那么虚拟内存就会占用的越多。
  • 系统参数限制,虽然 Linux 并没有内核参数来控制单个进程创建的最大线程个数,但是有系统级别的参数来控制整个系统的最大线程个数。

ulimit -a 命令查看进程创建线程时默认分配的栈空间大小,下面是 macOS 的输出,-s: stack size (kbytes)那一项就是了:

$ ulimit -a
……
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-v: address space (kbytes)          unlimited
-l: locked-in-memory size (kbytes)  unlimited
-u: processes                       2784
-n: file descriptors                256

调整创建线程时分配的栈空间大小,比如调整为 512k:

$ ulimit -s 512

有三个内核参数的大小,都会影响创建线程的上限:

  • /proc/sys/kernel/threads-max,表示系统支持的最大线程数,默认值是 14553;
  • /proc/sys/kernel/pid_max,表示系统全局的 PID 号数值的限制,每一个进程或线程都有 ID,ID 的值超过这个数,进程或线程就会创建失败,默认值是 32768;
  • /proc/sys/vm/max_map_count,用于限制一个进程能够拥有的虚拟内存映射区域(mmap)的数量。具体什么意思我也没搞清楚,反正如果它的值很小,也会导致创建线程失败,默认值是 65530。

1.2 线程崩溃,进程一定会崩溃吗

小林coding

1.2.1 会崩溃吗

一般来说如果线程是因为非法访问内存引起的崩溃,那么进程肯定会崩溃,为什么系统要让进程崩溃呢,这主要是因为在进程中,各个线程的地址空间是共享的,既然是共享,那么某个线程对地址的非法访问就会导致内存的不确定性,进而可能会影响到其他线程,这种操作是危险的,操作系统会认为这很可能导致一系列严重的后果,于是干脆让整个进程崩溃

1.2.2 进程是如何崩溃的-信号机制简介

发 kill 信号必须具有一定的权限,否则任意进程都可以通过发信号来终止其他进程,那显然是不合理的,实际上 kill 执行的是系统调用,将控制权转移给了内核(操作系统),由内核来给指定的进程发送信号。其背后的机制如下

  1. CPU 执行正常的进程指令
  2. 调用 kill 系统调用向进程发送信号
  3. 进程收到操作系统发的信号,CPU 暂停当前程序运行,并将控制权转交给操作系统
  4. 调用 kill 系统调用向进程发送信号(假设为 11,即 SIGSEGV,一般非法访问内存报的都是这个错误)
  5. 操作系统根据情况执行相应的信号处理程序(函数),一般执行完信号处理程序逻辑后会让进程退出

如果进程没有注册自己的信号处理函数,那么操作系统会执行默认的信号处理程序(一般最后会让进程退出),但如果注册了,则会执行自己的信号处理函数,它收到 kill 信号后,可以调用 exit() 来退出,但也可以使用 sigsetjmp,siglongjmp 这两个函数来恢复进程的执行。

在 Java 中,StackoverflowError 或者 NPE 都属于非法访问内存, JVM 之所以不会崩溃,其实就是因为 JVM 自定义了自己的信号处理函数,拦截了 SIGSEGV 信号,针对这两者不让它们崩溃。