线程栈默认大小通常为8mb(linux x86_64),java默认-xss1m;实际可用空间因guard page更小;ulimit -s查限制,pstack或/proc/pid/maps查使用量;栈溢出不一定是递归导致,可能是大局部变量、反射嵌套或jni调用;pthread_create手动设栈需对齐且谨慎;java可捕获stackoverflowerror,c/c++栈溢出直接sigsegv;混合场景下native栈崩溃jstack不可见。

线程栈到底占多少内存?别猜,用命令看
Linux 下每个线程默认栈大小不是“大概几百KB”,而是由内核和 glibc 共同决定的固定值——通常为 8MB(x86_64 系统常见),但实际可用空间往往更小,因为栈底附近有保护页(guard page);Java 默认是 -Xss1m(1MB),但不同 JVM 版本/平台可能不同(OpenJDK 17 在 Linux 上常为 1024k)。不查清楚就调参数,等于蒙眼调优。
实操建议:
- 查当前线程栈限制:
ulimit -s(单位 KB),这是进程级软限制,pthread_create创建的线程会受其约束 - 查某线程真实栈使用量:用
pstack <pid></pid>或cat /proc/<pid>/maps | grep stack</pid>看映射范围,再结合/proc/<pid>/stat</pid>中的stksize字段(需换算) - Java 线程栈大小必须统一在启动时设好:
java -Xss512k MyApp,运行中无法修改;且-Xss设置的是**每个线程**的栈,不是总和
StackOverflowError 不等于“递归写错了”
抛出 StackOverflowError 或触发 Segmentation fault (core dumped),未必是代码里写了无限递归。它只是“栈帧压不下了”的最终表现,背后可能是:
- 单个方法定义了超大局部变量(比如
byte[] buffer = new byte[1024 * 1024]在栈上分配引用+对象头,但若用new byte[...]是堆分配;真正危险的是栈上直接声明大数组:int[] arr = new int[100000]在局部变量表里占大量 slot) - 深度反射调用或代理链(如 Spring AOP 多层环绕通知嵌套 + 方法参数多)导致隐式栈帧累积
- C/C++ JNI 层调用未控制递归深度,而 JVM 栈和 native 栈是分开管理的,
-Xss对 native 栈无效 - Debug 模式下编译器不优化,
return语句没真正剪枝调用链(你删了递归入口却仍报错,很可能是调试信息保留了栈帧结构)
pthread_create 为什么一跑就段错误?
用 pthread_create 手动创建线程时,如果传入的栈空间不足或未对齐,程序很可能在启动瞬间崩在 clone 系统调用里,报 Segmentation fault 而非清晰错误——因为线程栈是通过 mmap 预分配的固定块,**不增长、不检查、不提示**,耗尽即越界。
实操建议:
- 不要手动传栈地址,除非你真需要控制布局;默认让 glibc 分配最安全
- 若必须指定栈:
malloc(2 * 1024 * 1024)后用posix_memalign对齐到sysconf(_SC_PAGESIZE),再传给pthread_attr_setstack - 验证栈是否够用:在新线程入口函数开头写
char dummy[1024*1024]; dummy[0] = 1;,逐步放大测试边界 - 注意:子线程栈虽“私有”,但仍在进程地址空间内,其他线程能读写它——这不是 bug,是设计使然,但要自己加同步
Java 和 C++ 的栈溢出,救法完全不同
Java 的 StackOverflowError 是 JVM 主动检测并抛异常,你能 catch、打印栈轨迹、甚至做降级逻辑;C/C++ 的栈溢出是硬件级访问违规,直接 SIGSEGV,连信号处理器都未必来得及注册——因为栈已破坏到无法保存上下文。
这意味着:
- Java 可靠的防御手段只有两个:
-Xss调大 + 代码层加递归深度计数器(比如传入int depth参数,超阈值提前返回) - C++ 没有“栈容量检查”机制,唯一靠谱做法是避免深度递归,改用显式栈(
std::stack<state></state>)+ 循环;或用setrlimit(RLIMIT_STACK, ...)提前设上限,让崩溃更早暴露 - 混合场景(JNI)最危险:JVM 栈够用,native 栈爆了,整个进程跪,
jstack看不到线索,得靠gdb --pid <pid></pid>+info registers查rsp是否异常偏移
栈不是越大越好——开太多线程配大栈,内存吃紧;开太少又卡并发。平衡点不在文档里,在你 perf record -e page-faults 抓到的缺页中断次数里。










