Objective-C try/catch异常处理机制原理。
try-catch-finaly
finally在任何情况下都会执行(不管有没有异常),属于整个体系的附属。
基本思想是跳到捕获锚点,重新执行。
http://www.cnblogs.com/markhy/p/3169035.html
Objective-C使用@try @catch @finally来捕获并处理异常。处理异常需要用到NSException类,它是所有异常的基类。你可以直接使用NSException类来捕获异常,也可以继承一个新的类。
Objective-C是C语言的扩充,它的异常处理机制是通过C标准库提供两个特殊的函数setjmp()和longjmp()函数实现的。如果对C的异常处理机制和setjmp、longjmp函数不了解的,建议先阅读:C语言异常处理机制。
先来看看下面的例子:

#import <Foundation/Foundation.h> int main (int argc, const char * argv[])
{
@autoreleasepool
{
@try {
NSException *e = [NSException
exceptionWithName:@"FileNotFoundException"
reason:@"File Not Found on System"
userInfo:nil];
@throw e;
}
@catch (NSException *exception) {
if ([[exception name] isEqualToString:NSInvalidArgumentException]) {
NSLog(@"%@", exception);
} else {
@throw exception;
}
}
@finally {
NSLog(@"finally");
}
}
return 0;
}

例子很简单,在@try中抛出一个自定义的FileNotFoundException类型的异常,然后在@catch中判断捕获的异常是不是NSInvalidArgumentException类型,如果不是,将异常再次抛出。最后总是会执行@finally语句,一般异常处理的善后工作都放这里来做。
如何才能了解它内部的工作流程,@try @catch @finally的定义无法查看。幸运的是我们可以通过Clang生成C的中间代码来了解try/catch原理。想了解Clang推荐阅读:编译器Clang介绍。
以上面的代码为例,使用文本编辑器将代码保存到main.m文件中,文件名可随便定义。打开终端输入:clang -rewrite-objc main.m 命令编译。
得到一份main.cpp文件:

struct objc_selector; struct objc_class;
struct __rw_objc_super { struct objc_object *object; struct objc_object *superClass; };
#ifndef _REWRITER_typedef_Protocol
typedef struct objc_object Protocol;
#define _REWRITER_typedef_Protocol
#endif
#define __OBJC_RW_DLLIMPORT extern
__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend(struct objc_object *, struct objc_selector *, ...);
__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper(struct objc_super *, struct objc_selector *, ...);
__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend_stret(struct objc_object *, struct objc_selector *, ...);
__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper_stret(struct objc_super *, struct objc_selector *, ...);
__OBJC_RW_DLLIMPORT double objc_msgSend_fpret(struct objc_object *, struct objc_selector *, ...);
__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass(const char *);
__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *);
__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass(const char *);
__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);
__OBJC_RW_DLLIMPORT void objc_exception_try_enter(void *);
__OBJC_RW_DLLIMPORT void objc_exception_try_exit(void *);
__OBJC_RW_DLLIMPORT struct objc_object *objc_exception_extract(void *);
__OBJC_RW_DLLIMPORT int objc_exception_match(struct objc_class *, struct objc_object *);
__OBJC_RW_DLLIMPORT void objc_sync_enter(struct objc_object *);
__OBJC_RW_DLLIMPORT void objc_sync_exit(struct objc_object *);
__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);
#ifndef __FASTENUMERATIONSTATE
struct __objcFastEnumerationState {
unsigned long state;
void **itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);
#define __FASTENUMERATIONSTATE
#endif
#ifndef __NSCONSTANTSTRINGIMPL
struct __NSConstantStringImpl {
int *isa;
int flags;
char *str;
long length;
};
#ifdef CF_EXPORT_CONSTANT_STRING
extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];
#else
__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];
#endif
#define __NSCONSTANTSTRINGIMPL
#endif
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// Runtime copy/destroy helper functions (from Block_private.h)
#ifdef __OBJC_EXPORT_BLOCKS
extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];
extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];
#else
__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);
__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);
__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];
__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];
#endif
#endif
#define __block
#define __weak #define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
static __NSConstantStringImpl __NSConstantStringImpl_main_m_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"FileNotFoundException",21};
static __NSConstantStringImpl __NSConstantStringImpl_main_m_1 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"File Not Found on System",24};
static __NSConstantStringImpl __NSConstantStringImpl_main_m_2 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"%@",2};
static __NSConstantStringImpl __NSConstantStringImpl_main_m_3 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"finally",7};
//
// main.c
// TestBlock
//
// Created by xxxx on 13-6-2.
// Copyright (c) 2013 xxxx. All rights reserved.
// #include <Foundation/Foundation.h> int main (int argc, const char * argv[])
{
@autoreleasepool
{
/* @try scope begin */
{
struct _objc_exception_data
{
int buf[18/*32-bit i386*/];
char *pointers[4];
} _stack; id volatile _rethrow = 0;
objc_exception_try_enter(&_stack); if (!_setjmp(_stack.buf)) /* @try block continue */
{
NSException *e = ((NSException *(*)(id, SEL, NSString *, NSString *, NSDictionary *))(void *)objc_msgSend)(objc_getClass("NSException"), sel_registerName("exceptionWithName:reason:userInfo:"), (NSString *)&__NSConstantStringImpl_main_m_0, (NSString *)&__NSConstantStringImpl_main_m_1, (NSDictionary *)((void *)0)); objc_exception_throw(e); } /* @catch begin */ else { id _caught = objc_exception_extract(&_stack);
objc_exception_try_enter (&_stack); if (_setjmp(_stack.buf))
_rethrow = objc_exception_extract(&_stack); else { /* @catch continue */
if (objc_exception_match((struct objc_class *)objc_getClass("NSException"), (struct objc_object *)_caught)) {
NSException *exception = _caught; if (((BOOL (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)exception, sel_registerName("name")), sel_registerName("isEqualToString:"), (NSString *)NSInvalidArgumentException)) { NSLog((NSString *)&__NSConstantStringImpl_main_m_2, exception);
} else { objc_exception_throw( exception);
} } /* last catch end */ else {
_rethrow = _caught;
objc_exception_try_exit(&_stack);
}
} /* @catch end */
}
/* @finally */
{
if (!_rethrow) objc_exception_try_exit(&_stack); NSLog((NSString *)&__NSConstantStringImpl_main_m_3); if (_rethrow) objc_exception_throw(_rethrow);
} } /* @try scope end */ }
return 0;
}

文件中信息量太大,咱们只看main函数部分,下面的代码把main函数的代码作了注释说明:

#include <Foundation/Foundation.h> int main (int argc, const char * argv[])
{
@autoreleasepool
{
/**
* try/catch的作用域从这里开始
*/
/* @try scope begin */
{
/**
* 首先定义一个_objc_exception_data类型的结构体,用来保存异常现场的数据。
*/
struct _objc_exception_data
{
/**
* buf变量就是c语言中的jmp_buf
* jmp_buf的定义可在setjmp.h文件中找到:
*
* #define _JBLEN (10 + 16 + 2)
* #define _JBLEN_MAX _JBLEN
*
* typedef int jmp_buf[_JBLEN];
*/
int buf[18/*32-bit i386*/]; /**
* pointers[0]用来存储通过@throw抛出的异常对象,
* pointers[1]存储下一个_stack数据。
*/
char *pointers[4];
} _stack; /**
* _rethrow保存可能在@catch中再次抛出的异常对象。
*/
id volatile _rethrow = 0; /**
* 因为异常处理支持嵌套,_stack会被存储在一个全局的栈中,这个栈用单链表的存储结构表示。
* objc_exception_try_enter函数将_stack压栈。
*/
objc_exception_try_enter(&_stack); /**
* _setjmp是C的函数,用于保存当前程序现场。
* _setjmp需要传入一个jmp_buf参数,保存当前需要用到的寄存器的值。
* _setjmp()它能返回两次,第一次是初始化时,返回0,第二次遇到_longjmp()函数调用会返回,返回值由_longjmp的第二个参数决定。
* 如果对_setjmp()和_longjmp()概念不太了解的,请参考C语言的异常处理机制。
*
* 下面_setjmp()初始化返回0,然后执行if{}中也就是@try{}中的代码。
*/
if (!_setjmp(_stack.buf)) /* @try block continue */
{
/**
* 创建一个NSException对象,对应代码:
*
* NSException *e = [NSException
* exceptionWithName:@"FileNotFoundException"
* reason:@"File Not Found on System"
* userInfo:nil];
*/
NSException *e = ((NSException *(*)(id, SEL, NSString *, NSString *, NSDictionary *))(void *)objc_msgSend)(objc_getClass("NSException"), sel_registerName("exceptionWithName:reason:userInfo:"), (NSString *)&__NSConstantStringImpl_main_m_0, (NSString *)&__NSConstantStringImpl_main_m_1, (NSDictionary *)((void *)0)); /**
* 抛出异常对象,对应代码:@throw e;
*
* objc_exception_throw函数实现步骤如下:
* 1. 把e对象保存到_stack->pointers[0]中使其在@catch{}中能被捕获。
* 2. 将_stack从全局栈中弹出。
* 3. 调用_longjmp()跳转到前面if语句中的_setjmp()位置。_longjmp()使得_setjmp()函数第二次返回,
* 返回值为1,所以会执行else{}中也就是@catch{}中的代码。
*/
objc_exception_throw(e); } /* @catch begin */ else { /**
* objc_exception_extract函数从_stack->pointers[0]中取得上面抛出的异常对象。
*/
id _caught = objc_exception_extract(&_stack); /**
* 这里为何再次调用objc_exception_try_enter对_stack压栈?先保留这个疑问,继续看下面的代码。
*/
objc_exception_try_enter (&_stack); /**
* 在@catch中设置一个跳转位置
*/
if (_setjmp(_stack.buf)) /**
* 如果@catch{}中再次抛出异常,在这里捕获。
*/
_rethrow = objc_exception_extract(&_stack); else { /* @catch continue */ /**
* objc_exception_match函数判断_caught对象是否是需要捕获的目标对象。对应代码:
*
* @catch (NSException *exception) {
*/
if (objc_exception_match((struct objc_class *)objc_getClass("NSException"), (struct objc_object *)_caught)) {
NSException *exception = _caught; /**
* 比较捕获的异常是不是NSInvalidArgumentException类型。对应代码:
*
* if ([[exception name] isEqualToString:NSInvalidArgumentException]) {
* NSLog(@"%@", exception);
*
*/
if (((BOOL (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)exception, sel_registerName("name")), sel_registerName("isEqualToString:"), (NSString *)NSInvalidArgumentException)) { NSLog((NSString *)&__NSConstantStringImpl_main_m_2, exception);
} else { /**
* 抛出异常对象,然后跳转到前面@catch中的if语句中的_setjmp()位置。
* 这就解释了前面为什么要在@catch中再次将_stack压栈和调用_setjmp()的原因。
* 在当前@catch中,如果不设置一个跳转点来捕获@catch中抛出的异常,那么程序就直接跳转到全局栈的下一个@catch中,而下面的@finally{}代码就无法执行。
* 在@catch中设置跳转点就是为了最后总能执行@finally中的代码。
*/
objc_exception_throw( exception);
} } /* last catch end */ else { /**
* 如果异常对象没被处理,先将其保存到_rethrow变量。
* objc_exception_try_exit函数将_stack从全局栈中弹出。
*/
_rethrow = _caught;
objc_exception_try_exit(&_stack);
}
} /* @catch end */
}
/* @finally */
{
if (!_rethrow) objc_exception_try_exit(&_stack); NSLog((NSString *)&__NSConstantStringImpl_main_m_3); /**
* _rethrow是前面@catch中没有被处理的或被捕获的异常对象,
* 最后,_rethrow异常对象被抛到全局栈的下一个@catch中。
*/
if (_rethrow) objc_exception_throw(_rethrow);
} } /* @try scope end */ }
return 0;
}

以上代码中还涉及了objc_exception_try_enter、objc_exception_extract、objc_exception_throw、objc_exception_try_exit等函数,这些函数可以在苹果开源的objc4的objc-exception.mm文件中找到,objc4源码可在这里下载。下面代码只显示部分方便阅读:

typedef struct {
int version;
void (*throw_exc)(id); // version 0
void (*try_enter)(void *); // version 0
void (*try_exit)(void *); // version 0
id (*extract)(void *); // version 0
int (*match)(Class, id); // version 0
} objc_exception_functions_t; static objc_exception_functions_t xtab; // forward declaration
static void set_default_handlers(); /*
* Exported functions
*/ // get table; version tells how many
void objc_exception_get_functions(objc_exception_functions_t *table) {
// only version 0 supported at this point
if (table && table->version == 0)
*table = xtab;
} // set table
void objc_exception_set_functions(objc_exception_functions_t *table) {
// only version 0 supported at this point
if (table && table->version == 0)
xtab = *table;
} /*
* The following functions are
* synthesized by the compiler upon encountering language constructs
*/ void objc_exception_throw(id exception) {
if (!xtab.throw_exc) {
set_default_handlers();
} if (PrintExceptionThrow) {
_objc_inform("EXCEPTIONS: throwing %p (%s)",
exception, object_getClassName(exception));
void* callstack[500];
int frameCount = backtrace(callstack, 500);
backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
} OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
xtab.throw_exc(exception);
_objc_fatal("objc_exception_throw failed");
} void objc_exception_try_enter(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
xtab.try_enter(localExceptionData);
} void objc_exception_try_exit(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
xtab.try_exit(localExceptionData);
} id objc_exception_extract(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
return xtab.extract(localExceptionData);
} int objc_exception_match(Class exceptionClass, id exception) {
if (!xtab.throw_exc) {
set_default_handlers();
}
return xtab.match(exceptionClass, exception);
} // quick and dirty exception handling code
// default implementation - mostly a toy for use outside/before Foundation
// provides its implementation
// Perhaps the default implementation should just complain loudly and quit extern void _objc_inform(const char *fmt, ...); typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t; typedef struct _threadChain {
LocalData_t *topHandler;
objc_thread_t perThreadID;
struct _threadChain *next;
}
ThreadChainLink_t; static ThreadChainLink_t ThreadChainLink; static ThreadChainLink_t *getChainLink() {
// follow links until thread_self() found (someday) XXX
objc_thread_t self = thread_self();
ThreadChainLink_t *walker = &ThreadChainLink;
while (walker->perThreadID != self) {
if (walker->next != NULL) {
walker = walker->next;
continue;
}
// create a new one
// XXX not thread safe (!)
// XXX Also, we don't register to deallocate on thread death
walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
walker = walker->next;
walker->next = NULL;
walker->topHandler = NULL;
walker->perThreadID = self;
}
return walker;
} static void default_try_enter(void *localExceptionData) {
LocalData_t *data = (LocalData_t *)localExceptionData;
ThreadChainLink_t *chainLink = getChainLink();
data->pointers[1] = chainLink->topHandler;
chainLink->topHandler = data;
if (PrintExceptions) _objc_inform("EXCEPTIONS: entered try block %p\n", chainLink->topHandler);
} static void default_throw(id value) {
ThreadChainLink_t *chainLink = getChainLink();
LocalData_t *led;
if (value == nil) {
if (PrintExceptions) _objc_inform("EXCEPTIONS: objc_exception_throw with nil value\n");
return;
}
if (chainLink == NULL) {
if (PrintExceptions) _objc_inform("EXCEPTIONS: No handler in place!\n");
return;
}
if (PrintExceptions) _objc_inform("EXCEPTIONS: exception thrown, going to handler block %p\n", chainLink->topHandler);
led = chainLink->topHandler;
chainLink->topHandler = (LocalData_t *)
led->pointers[1]; // pop top handler
led->pointers[0] = value; // store exception that is thrown
#if TARGET_OS_WIN32
longjmp(led->buf, 1);
#else
_longjmp(led->buf, 1);
#endif
} static void default_try_exit(void *led) {
ThreadChainLink_t *chainLink = getChainLink();
if (!chainLink || led != chainLink->topHandler) {
if (PrintExceptions) _objc_inform("EXCEPTIONS: *** mismatched try block exit handlers\n");
return;
}
if (PrintExceptions) _objc_inform("EXCEPTIONS: removing try block handler %p\n", chainLink->topHandler);
chainLink->topHandler = (LocalData_t *)
chainLink->topHandler->pointers[1]; // pop top handler
} static id default_extract(void *localExceptionData) {
LocalData_t *led = (LocalData_t *)localExceptionData;
return (id)led->pointers[0];
} static int default_match(Class exceptionClass, id exception) {
//return [exception isKindOfClass:exceptionClass];
Class cls;
for (cls = _object_getClass(exception); nil != cls; cls = _class_getSuperclass(cls))
if (cls == exceptionClass) return 1;
return 0;
} static void set_default_handlers() {
objc_exception_functions_t default_functions = {
0, default_throw, default_try_enter, default_try_exit, default_extract, default_match }; // should this always print?
if (PrintExceptions) _objc_inform("EXCEPTIONS: *** Setting default (non-Foundation) exception mechanism\n");
objc_exception_set_functions(&default_functions);
} void exception_init(void)
{
// nothing to do
} void _destroyAltHandlerList(struct alt_handler_list *list)
{
// nothing to do
} // !__OBJC2__
#else
// __OBJC2__

Objective-C try/catch异常处理机制原理。的更多相关文章
- ASP.NET(C#)中的try catch异常处理机制
在开发一个Umbraco平台系统的过程中,遇到了问题. 写的代码如下 fileUrl = MediaHelper.GetMediaUrl(Convert.ToInt32(publishedConten ...
- try throw catch异常处理机制
/*本程序实现分块查找算法 又称索引顺序查找 需要注意的是分块查找需要2次查找 先对块查找 再对块内查找 2013.12.16 18:44*/ #include <io ...
- Atitit 异常机制与异常处理的原理与概论
Atitit 异常机制与异常处理的原理与概论 1. 异常vs 返回码1 1.1. 返回码模式的处理 (瀑布if 跳到失败1 1.2. 终止模式 vs 恢复模式(asp2 1.3. 异常机制的设计原理 ...
- java——关于异常处理机制的简单原理和应用
异常处理机制的简单原理和应用 一.Execption可以分为java标准定义的异常和程序员自定义异常2种 (1)一种是当程序违反了java语规则的时候,JAVA虚拟机就会将发生的错误表示为一个异常.这 ...
- springMVC源码分析--异常处理机制HandlerExceptionResolver执行原理(二)
上一篇博客springMVC源码分析--异常处理机制HandlerExceptionResolver简单示例(一)中我们简单地实现了一个异常处理实例,接下来我们要介绍一下HandlerExceptio ...
- Java基础-异常处理机制 及异常处理的五个关键字:try/catch/finally/throw /throws
笔记: /** 异常处理机制: 抓抛模型 * 1."抛", 一旦抛出,程序终止! printStackTrace()显示异常路径! * 2."抓", 抓住异常 ...
- 35 异常机制 异常处理机制 异常处理五个关键字 try、catch、finally、throw、thorws 代码
异常处理机制 概念 抛出异常 捕获异常 异常处理五个关键字 try.catch.finally.throw.thorws 代码 // main { int a = 1; int b = 0; // 假 ...
- Java中的异常处理机制的简单原理和应用?
程序运行过程中可能出现各种"非预期"情况,这些非预期情况可能导致程序非正常结束. 为了提高程序的健壮性,Java提供了异常处理机制: try { s1... s2... s3... ...
- SpringMVC异常处理机制详解[附带源码分析]
目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...
随机推荐
- uva1610 聚会游戏(细节处理)
uva1610 聚会游戏(细节处理) 输入一个n(n<=1000且为偶数)个字符串的集合D,找一个长度最短的字符串(不一定要在D中出现)S,使得D中恰好一半字符串小于等于S,另一半大于S.如果有 ...
- 【NOIP模拟赛】收银员(一道差分约束好题)
/* s[]表示最优方案的序列中的前缀和,那么s[23]就是最优方案 由题意我们可以列出这样一些式子: s[i]+s[23]-s[16+i]>=a[i] (i-8<0) s[i]-s[i- ...
- bzoj3876: [Ahoi2014&Jsoi2014]支线剧情(上下界费用流)
传送门 一道题让我又要学可行流又要学zkw费用流…… 考虑一下,原题可以转化为一个有向图,每次走一条路径,把每一条边都至少覆盖一次,求最小代价 因为一条边每走过一次,就要付出一次代价 那不就是费用流了 ...
- [Xcode 实际操作]四、常用控件-(11)UIDatePicker日期时间选择器
目录:[Swift]Xcode实际操作 本文将演示日期拾取器的使用. 使用日期拾取器,可以快速设置和选择日期与时间. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] ...
- [转]SE43 修改SAP标准菜单、登陆界面、背景图片
1.事务码se43 复制标准菜单S000 到 ZS000 2.按实际需要修改 ZS000 3.在事务码SSM2中用ZS000 代替 S000 4.注销后重新登陆 o 修改SAP登陆界面(在本博客 ...
- RABC(Role-Based Access Control) 基于角色的权限访问控制
基于角色的权限访问控制(Role-Based Access Control),通过角色绑定权限,然后给用户划分角色.在web应用中,可以将权限理解为url,一个权限对应一个url. 使用thinkph ...
- 你的php
最开始学习做网页,用的是HTML,现在开始php了,那么要想用php,首先你得安装对不对,那么问题来了,你上哪安去啊(一看小编就是东北人),那么小编给各位提供了两个链接(不要告诉其他人哦)https: ...
- 1095 Cars on Campus(30 分
Zhejiang University has 8 campuses and a lot of gates. From each gate we can collect the in/out time ...
- Java ping 主机 端口
22:04:08.002 [main] INFO Ping - 目标地址 192.168.1.107 是否可到达:true 22:04:08.006 [main] INFO Ping - 执行命令 p ...
- 记一次内存溢出java.lang.OutOfMemoryError: unable to create new native thread
一.问题: 春节将至,系统访问量进入高峰期.随之系统出现了异常:java.lang.OutOfMemoryError: unable to create new native thread.在解决这个 ...