※天道酬勤※

§水至清则无鱼,人至察则无徒§
posts - 65, comments - 11, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2011年6月16日


1:  首先把JDK Linux版本上传到服务器上.
2:  使用命令 jstack PID 命令打印出CPU占用过高进程的线程栈.
3: 使用top -H -p PID 命令查看对应进程是哪个线程占用CPU过高. 比如:

top - 17:23:50 up 12 days,  1:44,  8 users,  load average: 4.85, 3.56, 3.76
Tasks: 556 total,   1 running, 555 sleeping,   0 stopped,   0 zombie
Cpu(s):  7.4%us,  4.4%sy,  0.4%ni, 71.6%id, 15.5%wa,  0.0%hi,  0.6%si,  0.0%st
Mem:     32072M total,    31972M used,      100M free,      265M buffers
Swap:     2047M total,       81M used,     1965M free,    23734M cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
17363 webserve  20   0 4475m 2.9g  10m R   54  9.4 163:29.84 java
16452 webserve  20   0 4475m 2.9g  10m S    6  9.4   6:03.00 java
11283 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.35 java
11284 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.41 java
11285 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:20.89 java
11288 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.18 java
11291 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.04 java
11280 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:20.66 java
11281 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.41 java
11282 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.26 java
11286 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.36 java
11287 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.43 java
11289 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.80 java
11290 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:22.13 java
11292 webserve  20   0 4475m 2.9g  10m S    2  9.4   7:21.35 java
11293 webserve  20   0 4475m 2.9g  10m S    2  9.4  24:29.70 java
18727 webserve  20   0 4475m 2.9g  10m S    1  9.4   3:08.50 java
18812 webserve  20   0 4475m 2.9g  10m S    1  9.4   3:05.80 java
18829 webserve  20   0 4475m 2.9g  10m S    1  9.4   3:03.32 java
19888 webserve  20   0 4475m 2.9g  10m S    1  9.4   3:04.66 java


比如上面信息就可以看到 17363 线程CPU使用较高, 把这个值转换为 16 进制, 然后在第2步打印的
线程栈中进行搜索, 就可以知道这个线程栈是做什么的了.

posted @ 2011-06-16 17:27 五指魅力 阅读(3774) | 评论 (0)编辑 收藏

Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个java虚拟机。您可以选择自己的需要选择不同的操作系统和对应的JDK的版本(只要是符合Sun发布的Java规范的),但我们推荐您使用Sun公司发布的JDK。确保您所使用的版本是最新的,因为Sun公司和其它一些公司一直在为提高性能而对java虚拟机做一些升级改进。一些报告显示JDK1.4在性能上比JDK1.3提高了将近10%到20%。 

  可以给Java虚拟机设置使用的内存,但是如果你的选择不对的话,虚拟机不会补偿。可通过命令行的方式改变虚拟机使用内存的大小。如下表所示有两个参数用来设置虚拟机使用内存的大小。 

  参数 

  描述 

  -Xms 

  JVM初始化堆的大小 

  -Xmx 

  JVM堆的最大值 

  这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。 

  Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。 

  Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh的前面,增加如下设置: 

  JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】' 

  需要把这个两个参数值调大。例如: 

  JAVA_OPTS='-Xms256m -Xmx512m' 

  表示初始化内存为256MB,可以使用的最大内存为512MB。 

  另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。如果你把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。 

  如果系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。如果垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的详细输出,研究 垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的 80% 作为堆大小。当增加处理器时,记得增加内存,因为分配可以并行进行,而垃圾收集不是并行的。 

Tomcat 5常用优化和配置 

1、JDK内存优化: 

Tomcat默认可以使用的内存为128MB,Windows下,在文件{tomcat_home}/bin/catalina.bat,Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,增加如下设置: 

  JAVA_OPTS='-Xms[初始化内存大小] -Xmx[可以使用的最大内存] 

  一般说来,你应该使用物理内存的 80% 作为堆大小。 

2、连接器优化: 

  在tomcat配置文件server.xml中的配置中,和连接数相关的参数有: 

  maxThreads: 

  Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。默认值200。 

  acceptCount: 

  指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。 

  minSpareThreads: 

  Tomcat初始化时创建的线程数。默认值4。 

  maxSpareThreads: 

  一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值50。 

  enableLookups: 

  是否反查域名,默认值为true。为了提高处理能力,应设置为false 

  connnectionTimeout: 
  
  网络连接超时,默认值60000,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 

  maxKeepAliveRequests: 

  保持请求数量,默认值100。 

  bufferSize: 

  输入流缓冲大小,默认值2048 bytes。 

  compression: 

  压缩传输,取值on/off/force,默认值off。 

  其中和最大连接数相关的参数为maxThreads和acceptCount。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。 

3、tomcat中如何禁止和允许列目录下的文件 

  在{tomcat_home}/conf/web.xml中,把listings参数设置成false即可,如下:

posted @ 2011-06-16 17:13 五指魅力 阅读(284) | 评论 (0)编辑 收藏

2007年1月23日

AppFace For VC 支持Win9X/NT/2K/XP,UNICODE/ANSI,能够对目标进程里的所有Widows标准控件,系统菜单,通用对话框等实现换肤,对非商业用途而言,它是完全免费的。关键的是AppFace的使用非常简单,很容易添加到已有的工程中。可以到它的网站 http://www.appface.com 去下载最新版本 。


AppFace 支持三种皮肤加载方式,从磁盘文件加载,从资源加载,从内存加载。下面我就说说如何从资源加载URF皮肤文件:

首先,要确保你下载了AppFace for VC 开发包,我们要用到其中的三个文件: "appface.h", "appface.dll" ,"gtclassic.urf" 。

接着,创建一个 VC 可执行文件工程, MDI/SDI/DIALOG/WIN32 类型的都可以,我以创建一个DIALOG 工程 ResSkin 进行说明 。将"appface.h" 拷贝到该工程目录下,将"Appface.dll"拷贝到该工程的输出目录下,例如:"debug\",将"gtclassic.urf"拷贝到资源所在目录下 。在VC 的项目管理器中进入资源管理项,在右键菜单中选择"Import",将"gtclassic.urf"添加进工程,资源类型设定为: "MYRESTYPE",资源ID设定为"IDR_MY_URF"。注意:资源ID 前面一定要加双引号 。


准备工作全部完成了,现在进入正题:
1.在ResSkin.cpp 中加入: #include "appface.h"
2.在CResSkinApp::InitInstance() 成员函数中添加如下代码

CResSkinApp::InitInstance()
{
...
CAppFace af ; //line 1
af.Start(_T("IDR_MY_URF"),GTP_LOAD_RESOURCE,NULL,_T("MYTYPE"),NULL) ; //line 2

CResSkinDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();

...
af.Remove() ; //line 3

}

编译... 一切搞定 !

3. 上面是官方的做法,由于appface 也支持从内存中加载urf,所以您也可以这样做:

CResSkinApp::InitInstance()
{
...
CAppFace af ; //Init

//Load
if(HRSRC hr = ::FindResource(NULL,"IDR_MY_URF","MYTYPE"))
{
ULONG nResSize = ::SizeofResource(NULL,hr) ;
if(HGLOBAL hz = ::LoadResource(NULL,hr))
{
char* p = (char*)LockResource(hz);
if(p)
af.Start(p,GTP_LOAD_MEMORY,nResSize,NULL,NULL) ;
}
}

CResSkinDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();

...
af.Remove() ; //Release

}
你可以使用这种方法从资源中加载其它数据。


需要注意的是: 如果将URF添加进资源时,资源ID没有用双引号括起来, 加载时必须这样写:
af.Start(_T("IDR_MY_URF"),GTP_LOAD_RESOURCE,NULL,MAKEINTRESOURCE(_T("MYTYPE")),NULL)) ; //line 2



添加“换皮肤“按钮
添加一个Button,消息响应函数如下:

void CChatDlg::OnButton2()
{
    x++;    //全局变量,static int x=3;
    int x1;
    x1=x%3;
    if(x1==1)
    {
    CAppFace af ; 
    af.Start(_T("IDR_MY_URF1"),GTP_LOAD_RESOURCE,NULL,_T("MYTYPE"),NULL) ;
    this->MoveWindow(81,75,870,602);
    }
    if(x1==2)
    {
    CAppFace af ; 
    af.Start(_T("IDR_MY_URF2"),GTP_LOAD_RESOURCE,NULL,_T("MYTYPE"),NULL) ;
    this->MoveWindow(81,75,854,595);
    }
    if(x1==0)
    {
    CAppFace af ;
    af.Start(_T("IDR_MY_URF"),GTP_LOAD_RESOURCE,NULL,_T("MYTYPE"),NULL) ;
    this->MoveWindow(81,75,865,602);
    }
}

posted @ 2007-01-23 11:38 五指魅力 阅读(4160) | 评论 (1)编辑 收藏

2007年1月17日

使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

测试:
struct test
 {
  char a:1;
  char :2;
  long b:3;
  char c:2;
 };
 test t1;
 int len=sizeof(t1);   //len=12

struct test
 {
  char a:1;
  char :2;
  char b:3;
  long c:2;
 };
 test t1;
 int len=sizeof(t1);   //len=8

struct test
 {
  char a:1;
  char :2;
  char b:3;
  char c:2;
 };
 test t1;
 int len=sizeof(t1);   //len=1

posted @ 2007-01-17 11:18 五指魅力 阅读(2022) | 评论 (0)编辑 收藏

2006年12月5日

首先声明,简单的比较前缀自增运算符和后缀自增运算符的效率是片面的,因为存在很多因素影响这个问题的答案。  
   
   
  首先考虑内建数据类型的情况:  
   
  如果自增运算表达式的结果没有被使用,而仅仅简单的用于增加一员操作数,答案是明确的,前缀法和后缀法没有任何区别,编译器的处理都应该是相同的,很难想象得出有什么编译器实现可以别出心裁在二者之间制造任何差异。  
  测试C++源代码如下:  
  //test1.cpp  
  void   test()  
  {  
  int   i=0;  
  i++;  
  ++i;  
  }  
  Gnu   C/C++   2编译的汇编中间代码如下:  
                  .file       "test1.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
                  .align   4  
  .globl   _test__Fv  
                  .def         _test__Fv;             .scl         2;             .type       32;           .endef  
  _test__Fv:  
                  pushl   %ebp  
                  movl   %esp,%ebp  
                  subl   $24,%esp  
                  movl   $0,-4(%ebp) ;i=0  
                  incl   -4(%ebp) ;i++  
                  incl   -4(%ebp) ;++i  
                  jmp   L3  
                  jmp   L2  
                  .p2align   4,,7  
  L3:  
  L2:  
                  leave  
                  ret  
  很显然,不管是i++还是++i都仅仅是一条incl指令而已。  
   
  如果表达式的结果被使用,那么情况要稍微复杂一些。  
  测试C++源代码如下:  
  //test2.cpp  
  void   test()  
  {  
  int   i=0,a,b;  
  a=i++;  
  b=++i;  
  }  
  Gnu   C/C++   2编译的汇编中间代码如下:  
  .file "test2.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
  .align   4  
  .globl   _test__Fv  
  .def _test__Fv; .scl 2; .type 32; .endef  
  _test__Fv:  
  pushl   %ebp  
  movl   %esp,%ebp  
  subl   $24,%esp  
  movl   $0,-4(%ebp) ;i=0  
  movl   -4(%ebp),%eax ;i   -->   ax  
  movl   %eax,-8(%ebp) ;ax   -->   a(a=i)  
  incl   -4(%ebp) ;i++  
  incl   -4(%ebp) ;++i  
  movl   -4(%ebp),%eax ;i   -->   ax  
  movl   %eax,-12(%ebp) ;ax   -->   b(b=i)  
  jmp   L3  
  jmp   L2  
  .p2align   4,,7  
  L3:  
  L2:  
  leave  
  ret  
  有差别吗?显然也没有,同样是一条incl指令,再加上两条movl指令借用eax寄存器复制调用栈内容。  
   
  让我们再加上编译器优化,重新编译后的汇编代码如下:  
  .file "test2.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
  .align   4  
  .globl   _test__Fv  
  .def _test__Fv; .scl 2; .type 32; .endef  
  _test__Fv:  
  pushl   %ebp  
  movl   %esp,%ebp  
  leave  
  ret  
  好了,优化的过火了,由于i,a,b三个变量没有被使用,所以干脆全都被优化了,结果成了一个什么都不做的空函数体。  
   
  那么,让我们再加上一点代码使用a和b的结果吧,这样i的结果也不能够忽略了,C++源代码如下:  
  //test3.cpp  
  int   test()  
  {  
  int   i=0,a,b;  
  a=i++;  
  b=++i;  
  return   a+b;  
  }  
  此时汇编代码如下:  
  .file "test3.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
  .align   4  
  .globl   _test__Fv  
  .def _test__Fv; .scl 2; .type 32; .endef  
  _test__Fv:  
  pushl   %ebp  
  movl   %esp,%ebp  
  movl   $2,%eax  
  leave  
  ret  
  你还是没有想到吧,答案仅仅是编译器计算了返回值,常量展开(constant-unwinding)启动,变成了直接返回常量结果。  
   
  怎么办?我们把i变成参数,避免这种预期以外的结果,C++源代码如下:  
  //test4.cpp  
  int   test1(int   i)  
  {  
  int   a=i++;  
  return   a;  
  }  
   
  int   test2(int   i)  
  {  
  int   a=++i;  
  return   a;  
  }  
  好了,很辛苦,终于得到了不一样的汇编代码:  
  .file "test4.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
  .align   4  
  .globl   _test1__Fi  
  .def _test1__Fi; .scl 2; .type 32; .endef  
  _test1__Fi:  
  pushl   %ebp  
  movl   %esp,%ebp  
  movl   8(%ebp),%eax  
  leave  
  ret  
  .align   4  
  .globl   _test2__Fi  
  .def _test2__Fi; .scl 2; .type 32; .endef  
  _test2__Fi:  
  pushl   %ebp  
  movl   %esp,%ebp  
  movl   8(%ebp),%eax  
  incl   %eax  
  leave  
  ret  
  和你接触到的教条正相反吧,++i反而增加了一条汇编指令incl,而i++却没有,这就是编译器优化的魅力。  
  因为不管i有没有增加,都不影响a的值,而函数仅仅返回i的值,所以i的自增运算就根本不必进行了。  
  所以,为了更客观一些,我们将i参数改为按照引用传递,C++源代码如下;  
  //test5.cpp  
  int   test1(int   &i)  
  {  
  int   a=i++;  
  return   a;  
  }  
   
  int   test2(int   &i)  
  {  
  int   a=++i;  
  return   a;  
  }  
  这一次的结果加入了指针的运算,稍微复杂一些:  
  .file "test5.cpp"  
  gcc2_compiled.:  
  ___gnu_compiled_cplusplus:  
  .text  
  .align   4  
  .globl   _test1__FRi  
  .def _test1__FRi; .scl 2; .type 32; .endef  
  _test1__FRi:  
  pushl   %ebp  
  movl   %esp,%ebp  
  movl   8(%ebp),%eax  
  movl   (%eax),%edx  
  incl   (%eax)  
  movl   %edx,%eax  
  leave  
  ret  
  .align   4  
  .globl   _test2__FRi  
  .def _test2__FRi; .scl 2; .type 32; .endef  
  _test2__FRi:  
  pushl   %ebp  
  movl   %esp,%ebp  
  movl   8(%ebp),%eax  
  movl   (%eax),%edx  
  leal   1(%edx),%ecx  
  movl   %ecx,(%eax)  
  movl   %ecx,%eax  
  leave  
  ret  
  惊讶吗?还是a=i++的代码更高效一些,不知道这会让你有什么想法。反正,我得出的结论,对于内建数据类型来说,i++和++i孰优孰劣,是编译器实现相关的,实在不必太可以关心这个问题。  
   
   
  最后让我们再回到起点,对于自定义数据类型(主要是指类)说,不需要再做很多汇编代码的分析了,我很清楚的知道为什么会有人循循善诱。  
  因为前缀式可以返回对象的引用,而后缀式必须返回对象的值,所以导致了在大对象的时候产生了较大的复制开销,引起效率降低,因此会有劝告尽量使用前缀式,尽可能避免后缀式,除非从行为上真的需要后缀式。  
  这也就是More   Effective   C++/Term   7中的原文提到的,处理使用者自定义类型(注意不是指内建类型)的时候,应该尽可能的使用前缀式地增/递减,因为他天生体质较佳。  
  同时,为了保证前缀和后缀对递增/递减的语义的实现保持一致,设计上的一般原则是后缀式的实现以前缀式为基础,这样,后缀式往往多了一次函数调用,这也许也是一个需要考虑的效率因素,不过相比之下,就有点微乎其微了。  
  重申一点关于这个问题的进一步叙述,可以在Scott   Mayer的<<More   Effective   C++>>一书的条款7中获得,大约在原书的P31-34上。

posted @ 2006-12-05 14:38 五指魅力 阅读(777) | 评论 (1)编辑 收藏

2006年8月21日

1:程序设计或软件设计就是程序体系结构的规范,要实现这个规范来满足程序的功能和性能需求。以设计文档来体现。

设计文档包含的内容:
(1):将程序部分划为多个子系统,包括子系统间的接口和依赖关系,子系统间的数据流,在各子系统之间来回的输入和输出,以及总的线程模式。

(2):各子系统的具体细节,包括进一步细分的类,类层次关系,数据结构,算法,特定的线城模型和错误处理细节。

2:C++设计的两个原则:抽象(abstraction)和重用(reuse)

posted @ 2006-08-21 16:56 五指魅力 阅读(457) | 评论 (0)编辑 收藏

五大内存分区

C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)

明确区分堆与栈

bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。

首先,我们举一个例子:

void f() { int* p=new int[5]; }

这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:

00401028push14h

0040102Acalloperator new (00401060)

0040102Faddesp,4

00401032movdword ptr [ebp-8],eax

00401035moveax,dword ptr [ebp-8]

00401038movdword ptr [ebp-4],eax

这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。

好了,我们回到我们的主题:堆和栈究竟有什么区别?

主要的区别由以下几点:

1 、管理方式不同;

2 、空间大小不同;

3 、能否产生碎片不同;

4 、生长方向不同;

5 、分配方式不同;

6 、分配效率不同;

管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak

空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:

打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit

注意:reserve最小值为4Bytecommit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的

posted @ 2006-08-21 16:29 五指魅力 阅读(1729) | 评论 (0)编辑 收藏

1、什么是static?
  static 是c++中很常用的修饰符,它被用来控制变量的存储方式和可见性。

 2、为什么要引入static?
  函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。

3、什么时候用static?
  需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。

 4、static的内部机制:
  静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

  这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。

   静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。

  static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

5、static的优势:
  可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

 6、引用静态数据成员时,采用如下格式:
         <类名>::<静态成员名>
  如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。

7、注意事项:
  (1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。

   (2)不能将静态成员函数定义为虚函数。

   (3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。

   (4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将c++和c-based x window系统结合,同时也成功的应用于线程函数身上。

   (5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。

   (6)静态数据成员在<定义或说明>时前面加关键字static。

   (7)静态数据成员是静态存储的,所以必须对它进行初始化。

   (8)静态成员初始化与一般数据成员初始化不同:
                  初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;

     初始化时不加该成员的访问权限控制符private,public等;

     初始化时使用作用域运算符来标明它所属类;

     所以我们得出静态数据成员初始化的格式:
        <数据类型><类名>::<静态数据成员名>=<值>

   (9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。

posted @ 2006-08-21 15:43 五指魅力 阅读(472) | 评论 (0)编辑 收藏

1:构建 C++ 程序的步骤:首先,代码通过一个预处理器运行。这个预处理器会识别出代码的元信息。其次,代码经过编译或翻译为机器可读的对象文件。最后,单个的对象文件链接到一起,构成一个应用。

2:声明(declaration)只是告诉编译器如何调用一个函数,定义(definition)则包含了函数的实际代码。

3:命名空间(namespace)可以解决不同代码之间的命名冲突问题。但如果命名空间使用不恰当,仍然会造成命名冲突,如:两个命名空间都含有相同的名字。

4:C++ 在计算表达式时使用一种“短路逻辑”,这说明,一旦能够确认最后结果,表达式的余下部分就不必再计算了。

5:动态分配一个数组,首先需要声明一个指针。
int *myArr;
myArr=new int[arraySize];
分配了内存后就可以把myArr当作一个常规数组使用。
myArr[3]=2;
delete [] myArr;

6:
int *myInt=new int;
myInt = 8;          //指针的地址指向地址为8的内存空间,可能产生垃圾
*myInt = 8; //给指针赋值为8

7:字符串
C风格: char *str="this is a test";
 char str[20]="this is a test";
C++风格:
include <string>
string str="this is a test"; //可直接使用 =,==,+等运算符

8:const常量,保护变量不被修改,
const引用,好处:提交效率。免去建立副本的开销,也防止被修改。

posted @ 2006-08-21 11:11 五指魅力 阅读(620) | 评论 (0)编辑 收藏

2006年8月18日

ASCII表

ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符
0 NUT 32 (space) 64 @ 96
1 SOH 33 65 A 97 a
2 STX 34 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 , 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DCI 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 X 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 TB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 \ 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 ~
31 US 63 ? 95 127 DEL

posted @ 2006-08-18 22:36 五指魅力 阅读(424) | 评论 (0)编辑 收藏