OpenCASCADE开发指南<五>:OCC 内存管理器和异常类

  一个软件首先要规定能处理的数据类型, 其次要实现三项最基本的功能——引用管理、内存管理和异常管理。在 OCC 中,这三项功能分别对应基础类中的句柄、内存管理器和异常类。

在这里插入图片描述

1 异常类

1. 1 异常类的定义

  异常处理机制实现了正常程序逻辑与错误处理的分离, 提高了程序的可阅读性和执行效率。为了转移程序运行的控制流,异常处理的模式通常有无条件转移模式、 重试模式、 恢复模式和终止模式[5]。 与 C++一样, OCC 采用的是终止模式。

  为了实现这种异常处理机制, OCC 提供了一套异常类。所有异常类都是基于它们的根类——Failure 类的。异常类描述了函数运行期间可能发生的异常情况。发生异常时,程序将不能正常运行。对这种情况的响应被称为异常处理。

1. 2 异常类的使用

  OCC 使用异常的语法与 C++使用异常的语法相似。要产生一个确定类型的
异常,需用到相应异常类的 Raise()方法,如例 3.10 所示。

例 3.10:

DomainError::Raise(“Cannot cope with this condition”);

  这样就产生了一个 DomainError 类型的异常,同时伴有相应的提示信息“Cannot cope with this condition”。这信息可以是任意的。该异常可以被某种 DomainError 类型(DomainError 类型派生了好些类型)的句柄器捕获,如例 3.11 所示。
例 3.11:

try
{
OCC_CATCH_SIGNALS
// try 块。
}
catch (DomainError)
{
// 处理 DomainError 异常。
}

  不能把异常类的使用当作一种编程技巧, 例如用异常类代替“goto”。 应该把异常类的使用作为方法的一种保护方式(防止被错误使用),即保证方法调用者遇到的问题是方法能处理的。在程序正常运行期间,不该产生任何异常。

  在使用异常类的时候,需要用一个方法来保护另外一个可能出现异常的方
法。 这样能通过外层方法来检查内层方法的调用是否有效。 例如需要用三个方法(用于检查元素的 Value 函数、 用于检查数组下边界的 Lower 函数和用于检查数组上边界的 Upper 函数)使用 TCollection_ Array1 类,那么, Value 函数可以如例 3.12 那样被实现:

例 3.12:

Item TCollection_Array1::Value (const Standard_Integer&index) const
{
// 下面的 r1 和 r2 是数组的上下边界。
if(index < r1 || index > r2)
{
OutOfRange::Raise(“Index out of range in Array1::Value”);
}
return contents[index];
}

  在此, OutOfRange::Raise(“Index out of range in Array1::Value”)异常用 Lower函数和 Upper 函数检查索引是否有效,以保护 Value 函数的调用。

  一般地, 在 Value()函数调用前, 程序员已确定索引在有效区间内了。 这样,上面 Value()函数的实现就不是最优的了,因为检查既费时又冗余。在软件开发中有这样一种广泛的应用方式, 即将一些保护措施置于反汇编构件而非优化构件中。为了支持这种应用, OCC 为每一个异常类提供了相应的宏

Raise_if()<ErrorTypeName>_Raise_if(condition, “Error message”)

  这里 ErrorTypeName 是异常类型, condition 是产生异常的逻辑表达式,而 Error message 则是相关的错误信息。可以在编译的时候,通过 No_Exception 或者 No_两种预处理声明之一解除异常的调用,如例 3.13 所示:

例 3.13:

#define No_Exception /*解除所有的异常调用*/

使用这构造语句, Value 函数变为:

例 3.14:

Item TCollection_Array1::Value (const Standard_Integer&index) const
{
OutOfRange_Raise_if(index < r1 || index > r2,
“index out of range in Array1::Value”);
return contents[index];
}

1. 3 异常处理

  异常发生时, 控制点将转移到调用堆栈中离当前执行点最近的指定类型的句柄器上。该句柄器具有如下特征:

(1)它的 try 块刚刚被进入还没有被退出;
(2)它的类型与异常类型匹配。
(3) T 类型异常句柄器与 E 类型异常匹配,即 T 类型和 E 类型相同,或者T类型是 E 类型的超类型。

  OCC 的异常处理机制还可以将系统信号当作异常处理。为此,需要在相关代码的开端嵌入宏 OCC_CATCH_SIGNALS。建议将这个宏放在 try {}块中的第一位置。 例如, 有这样四个异常: NumericError 类型异常、 Overflow 类型异常、Underflow 类型异常和 ZeroDivide 类型异常, 其中 NumericError 类型是其它三种类型的超类型,那么,异常处理过程如例 3.15 所示。

例 3.15:

void f(1)
{
try
{
OCC_CATCH_SIGNALS
// try 块
}
catch(Standard_Overflow)
{ // 第一个句柄器
// ...
}
catch(Standard_NumericError)

}

  在这个例子中,第一个句柄器将捕获 Overflow 类型异常;第二个句柄器将
捕获 NumericError 类型异常及其派生异常,包括 Underflow 类型异常 和Zerodivide 类型异常。 异常发生时, 系统将从最近的 try 块到最远的 try 块逐一检查句柄器,直到找到一个在形式上与产生的异常相匹配的为止。

  在 try 块中, 如果将基类异常的句柄器置于派生类异常的句柄器之前, 则将发生错误。因为那样会导致后者永远不会被调用,如例 3.16 所示。
例 3.16:

void f(1)
{
int i = 0;
try
{
OCC_CATCH_SIGNALS
g(i);
// i 是可接受的。
}
// 在这放执行语句会导致编译错误!
catch(Standard_NumericError)
{
// 依据 i 值处理异常。
}
// 在这放执行语句可能导致不可预料的影响。
}

  由异常类形成的树状体系与用户定义的类完全无关。 该体系的根类是 Failure异常。 因此, Failure 异常句柄器可以捕获任何 OCC 异常。 建议将 Failure 异常句柄器设置在主路径中,如例 3.17 所示。

例 3.17:

#include <Standard_ErrorHandler.hxx>
#include <Standard_Failure.hxx>
#include <iostream.h>
int main (int argc, char* argv[ ])
{
Try
{
OCC_CATCH_SIGNALS
//主块
return 0;
}
catch(Standard_Failure)
{
Handle(Standard_Failure) error = Failure::Caught ();
cout << error << end1;
return 1;
}
}

  这里的 Caught 函数是 Failure 类的一个静态成员, 能返回一个含有异常错误信息的异常对象。这种接收对象的方法(通过 catch 的参数接收异常)代替了通常的 C++语句。

  尽管标准的 C++处理法则和语法在 try 块和句柄器中同样适用, 但在一些平台上, OCC 能以一种兼容模式被编译(此时异常支持长转移)。在这种模式中,要求句柄器前后没有执行语句。因此,强烈建议将 try 块置于{}中。此外,这种模式也要求 Standard_ErrorHandler.hxx 头文件包含在程序中(置于 try 块前), 否则将不能处理 OCC 异常。再有, catch()语句不允许将一个异常对象作为参数来传递。

  为了使程序能够像捕获其它异常那样捕获系统信号(如除零),在程序运行时要使用 OSD::SetSignal()方法安装相应的信号句柄器。通常,该方法在主函数
开端处被调用。

  为了能真正的将系统信号转换成 OCC 异常, OCC_CATCH_ SIGNALS 宏
应该被嵌入到源代码中。典型的,将该宏置于捕获异常的 try{}块的开端处。OCC 的异常处理机制依据不同的宏预处理 NO_CXX_EXCE- PTIONS 和OCC_CONVERT_SIGNALS 有不同的实现。 这些预处理将被 OCC 或者用户程序的编译程序连贯定义。在 Windows 和 DEC 平台上,这些宏不是以默认值被定义的, 并且所有类都支持 C++异常, 包括从句柄器中抛掷异常。 因此, 异常的处理与 C++异常处理一样。

2 内存管理器

2. 1 使用内存管理器的原因

  标准的内存分配有三种方式: 静态分配、 栈分配和堆分配。 静态分配是最简单的内存分配策略。 程序中的所有名字在编译时绑定在某个存储位置上; 这些绑定不会在运行时改变。块结构语言通过在栈上分配内存,克服了静态分配的一些限制。

  每次过程调用时,一个活动记录或是帧被压入系统栈,并在返回时弹出。堆分配与栈所遵循的后进先出的规律不同,堆中的数据结构能够以任意次序分配与释放。

  建模程序在运行期间,需要构造和析构大量的动态对象。在这种情况下, 标准的内存分配函数可能无法胜任工作。 因此, OCC 采用了特殊的内存管理器(在Standard 包中实现)。

2.2 内存管理器的用法

  在 C 代码中使用 OCC 内存管理器分配内存,只需用 Standard::Allocate()方法代替 malloc() 函数, Standard::Free() 方法代替 free() 函数,以 及
Standard::Reallocate()代替 realloc()函数。

  在 C++中, 可以将类的 new()操作定义为使用 Standard::Allocate()方法进行内存分配, 而将类的 delete()操作定义为使用 Standard::Allocate()方法进行内存释放。这样就可以使用 OCC 内存管理器为所有对象和所有类分配内存。就是用这种方式, CDL 提取器为所有用 CDL 声明的类定义了 new()函数和 delete()函数。 因此,除了异常类,所有 OCC 类都使用 OCC 内存管理器进行存储分配。

  因为 new()函数和 delete()函数是被继承的,所以对于所有 OCC 类的派生类(比如, Standard_Transient 类的派生类), new()函数和 delete()函数同样适用。

2.3 内存管理器的配置

  OCC 内存管理器可以适用于不同的内存优化技术(不同的内存块采用不同的优化技术, 这主要依据内存块的大小而定)。 或者, 用 OCC 内存管理器, 甚至可以不采用任何优化技术而直接使用 C 函数 malloc() 和 free()。

  内存管理器的配置由下面几个环境变量值定义:

(1) MMGT_OPT。如果值设为 1(默认值),则内存管理器将如下面的描述那样对内存进行优化。 如果值设为 0, 则每个内存块将直接分配(通过 malloc()和 free()函数)在 C 内存堆里。在第二种情况下, 所有异常(不包括 MMGT_CLEAR异常)都将被忽略。

(2) MMGT_CLEAR。 如果值设为 1(默认值), 则每一个已分配的内存块都将被清零。如果值设为 0,则内存块正常返回。

(3) MMGT_CELLSIZE。 它定义了大内存池中内存块的最大空间。 默认值是 200 字节。
(4) MMGT_NBPAGES。它定义了页面中由小内存块构成的内存组件(内存池的大小(由操作系统决定)。默认值是 1000 字节。

(5) MMGT_THRESHOLD。它定义了内存块(能直接在 OCC 内部被循环使用)的最大空间。默认值是 40000 字节。

(6) MMGT_MMAP。 当值设为 1(默认值) 时, 使用操作系统的映射函数对大内存块进行分配。 当值设为 0 时, 大内存块将被 malloc()分配在 C 内存堆里。

(7) MMGT_REENTRANT。 当值设为 1 时, 所有对内存优化管理器的调用都将被响应, 以保证不同的线程能同时访问内存管理器。 在多线程程序中, 这个变量值应该设置为 1。这里所说的多线程程序是指那些使用 OCC 内存管理器,并且可能有不止一个调用 OCC 函数的线程的程序。默认值是 0。

  在此提一个注意事项: 当多线程程序使用 OCC 以达到最佳内存优化性能时,需要检查两组变量。 其中一组是 MMGT_OPT=0; 另一组则是 MMGT_OPT=1 和
MMGT_REENTRANT=1。

2.4 内存管理器的实现

  当且仅当 MMGT_OPT=1 时, 才用到 OCC 的特殊的内存优化技术。 这些技术有:

(1) 小内存块( 空间比由 MMGT_CELLSIZE 设定的值小) 不能单独分配,而是分配在大内存池(大小由变量 MMGT_NBPAGES 决定)中。每一个内存块分配在当前内存池的空闲部分。若内存池被完全占据,则使用下一个内存池。 在当前版本中,在进程结束前,内存池不能返回操作系统。然而,那些由 Standard::Free()释放的内存块被记忆在释放列表中。 当需要下一个内存块(与列表中某个块大小相同) 时, 相应的被释放的那个内存块所占空间可以被新的内存块占用,这也叫内存块的循环使用。

( 2)对于中等大小的内存块(比 MMGT_CELLSIZE 大,但比 MMGT_THRESHOLD 小),它们是被直接分配(通过使用 malloc() 和 free())在C内存堆里。 这些块要是被 Standard::Free()方法释放的话, 可以像小内存块那样被循环使用。 然而, 与小内存块不同, 那些被记录在释放列表中的可循环使用的中内存块(由持有内存管理器的程序释放)可以被 Standard::Purge()方法返回到 C 内存堆中。

( 3)大内存块(大小比 MMGT_THRESHOLD 大,包括用来分配小内存块的内存池) 的分配取决于 MMGT_MMAP 的值。 如果该值是 0, 则这些大块被分配在 C 堆里。否则,它们被操作系统映射函数分配在内存映射文件中。 当Standard::Free()被调用时,大块立即被返回到操作系统中去。

2.5 内存管理器的优缺点

  OCC 内存管理器的优点主要体现在小块和中块的循环使用上。当程序需要连续分配和释放大小差不多的内存块时, 这个优点能加速程序的执行。 实际应用中,这种提升幅度可以高达 50%。

  相应的, OCC 内存管理器的主要缺点是:程序运行时,被循环使用的内存块不能返回到操作系统中。 这可能导致严重的内存消耗, 甚至会被操作系统误认为内存泄露。为了减少这种影响,在频繁地对内存进行操作后, OCC 系统将调用 Standard::Purge()方法。

  另外, OCC 内存管理器会带来额外的开销,它们有:

(1)舍入后,每个被分配的内存块的大小高达 8 字节。当 MMGT_OPT=0时,舍入值由 CRT 决定;对 32 位平台而言,典型值是 4 字节。

( 2) 在每个内存块的开端需要额外的 4 字节以记录该内存块的大小( 或者,当内存块被记录在释放列表时, 这 4 字节用来记录下一个内存块的地址)。 注意:只有在 MMGT_OPT=1 时,才需要这 4 字节。

  需要注意的是: 由 OCC 内存管理器带来的额外开销可能比由 C 内存堆管理器带来的额外开销大, 或者小。 因此, 整体而言, 很难说到底是优化模式的内存消耗大还是标准模式的内存消耗大——这得视情况而定。

  通常, 编程人员自己也会采用一种优化技术——在内存里面划出一些重要的块。 这样就可以将一些连续的数据存于这些块中, 使内存页面管理器对这些块的处理变得更容易。

  在多线程模式(MMGT_REENTRANT=1) 中, OCC 内存管理器使用互斥机制以锁定程序对释放列表的访问。 因此, 当不同的线程经常同时调用内存管理器时,优化模式的性能不如标准模式的性能好。原因是: malloc() 函数和 free()函数在实现的过程中开辟了几个分配空间——这样就避免了由互斥机制带来的延迟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/454146.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024春秋蓝桥杯reverse——crackme01

尝试了下输入没有任何反应 查看——32位——IDA打开 我之前没怎么写过win32&#xff0c;所以我开始在string里面找flag,wrong,right什么的字符&#xff0c;都不行 然后我又在函数里面找main&#xff0c;也什么收获的没有,OK废话完了 在win32里面 关于弹窗的函数&#xff1a;…

移动机器人设计与实践课程进度安排-2023-2024-2

进度安排由人工智能审核制定。 人工智能设计的机器人模型如下&#xff0c;一组三个&#xff1a; 轮式物流小车机器人智慧工厂绘图描述 背景: 绘制一个工厂的大致轮廓&#xff0c;包括工厂大门、围墙和主要的建筑结构。在背景中描绘一些工业设备、生产线和堆放的物料&#xff…

elasticsearch篇

1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 例如&#xff1a; 在电商网站搜索商品 在百度搜索答案 在打车软件搜索附近…

两个笔记本如何将一个笔记本作为另一个笔记本的拓展屏

需求是有两个笔记本&#xff0c;一个笔记本闲置&#xff0c;另一个笔记本是主力本。想将另一个闲置的笔记本连接到主力本上作为拓展屏使用。网上搜了好久&#xff0c;有一些人提到了&#xff0c;也有一些视频但是文章比较少。简单总结一下吧 上述需求有两种方式 第一种&#x…

Purple Pi OH鸿蒙开发板7天入门OpenHarmony开源鸿蒙教程【六】

今天我们来从OpenHarmony简介、环境搭建、创建第一个OpenHarmony项目等方面开始OpenHarmony应用开发的第一步。 一. OpenHarmony简介 OpenHarmony 是由开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目,目标是面向全场景、全连接、全智能…

BUGKU-WEB No one knows regex better than me

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 看到此类题目&#xff0c;直接代码审计 相关工具 base64 在线加密https://www.mklab.cn/utils/regex 解题步骤 代码审计 <?php error_reporting(0); # 从请求中获取了两个参数&#xff1…

String 底层是如何实现的?

1、典型回答 String 底层是基于数组实现的&#xff0c;并且数组使用了 final 修饰&#xff0c;不同版本中的数组类型也是不同的&#xff1a; JDK9 之前&#xff08;不含JDK9&#xff09; String 类是使用 char[ ]&#xff08;字符数组&#xff09;实现的但 JDK9 之后&#xf…

短视频解析接口分发系统

宝塔面板&#xff1a;Nginx系统 php7.2 Mysql 5.6-5.7 伪静态Thinkphp 上传文件直接访问域名安装即可 可以自备 听说后边要出saas去水印小程序 下载地址&#xff1a;https://pan.xunlei.com/s/VNskSEelfRVIzoSm5P5Rcw34A1?pwdqzhh# 接口演示&#xff1a; 前端演示…

安装PyTorch详细过程

安装anaconda 登录anaconda的官网下载&#xff0c;anaconda是一个集成的工具软件不需要我们再次下载。anaconda官网 跳转到这个页面如果你的Python版本正好是3.8版&#xff0c;那便可以直接根据系统去选择自己相应的下载版本就可以了。 但是如果你的Python版本号不是当前页面…

Spring 面试题及答案整理,最新面试题

Spring框架中的Bean生命周期是什么&#xff1f; Spring框架中的Bean生命周期包含以下关键步骤&#xff1a; 1、实例化Bean&#xff1a; 首先创建Bean的实例。 2、设置属性值&#xff1a; Spring框架通过反射机制注入属性。 3、调用BeanNameAware的setBeanName()&#xff1a…

数据库增删改查以及联合查询——数据库——day1

今天学习了数据库的知识 首先数据库分为关系型数据库和非关系型数据库 关系型数据库:Mysql OracleSqlServerSqlite 非关系型数据库&#xff1a;RedisNoSQL我们首先来看一下数组、链表、文件、数据库的区别 数组、链表: 内存存放数据的方式(代码运行结束、关机数据丢失) …

macOS安装maven

官网下载maven https://maven.apache.org/download.cgi如下图所示&#xff1a; 1.解压并复制到指定的目录中2.配置环境变量 vim ~/.bash_profile进行文件的修改 export M2_HOME/Users/fanfan/company/apache-maven-3.9.6 export PATH$PATH:$M2_HOME/bin3.终端中输入source ~…

echarts绘制 联系词(关键字)

<template><div><div>【关键词条】</div><div ref"target" class"w-full h-full" stylewidth:300px;height:300px></div></div> </template><script setup> import { ref, onMounted,watch } from …

数据结构——通讯录项目

1.通讯录的介绍 顺序表是通讯录的底层结构。 通讯录是将顺序表的类型替换成结构体类型来储存用户数据&#xff0c;通过运用顺序表结构来实现的。 用户数据结构&#xff1a; typedef struct PersonInfo {char name[12];char sex[10];int age;char tel[11];char addr[100]; }…

【C++】C++的初步认识

&#x1f338;博主主页&#xff1a;釉色清风&#x1f338;文章专栏&#xff1a;C&#x1f338;今日语录&#xff1a;自律以修身&#xff0c;自省以观己。自学以长识&#xff0c;自处以蓄力。 &#x1f33b;Hi~大家好&#xff0c;这次文章是C的初步认识&#xff0c;包括从C语言到…

(黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_高级篇_01&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——保护 今日目标1.初识Sentinel1.1.雪崩问题及解决方案1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.…

Mybatis Plus + Spring 分包配置 ClickHouse 和 Mysql 双数据源

目录 一、背景 二、各个配置文件总览&#xff08;文件位置因人而异&#xff09; 2.1 DataSourceConfig 2.2 MybatisClickHouseConfig &#xff08;ClickHouse 配置类&#xff09; 2.3 MybatisMysqlConfig&#xff08;Mysql 配置类&#xff09; 2.4 application.propertie…

Xterminal:未来的终端体验

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 开发环境篇 ✨特色专栏&#xff1a; M…

Git版本控制工具简介

版本控制工具 分类 集中式版本控制工具 CVS、SVN、VSS 缺点&#xff1a;如果服务器一旦宕机&#xff0c;所有历史数据将会丢失 分布式版本控制工具 Git、Mercurial、Bazaar、Darcs...... 优势&#xff1a;本地即可进行版本控制&#xff0c;很好避免了单点故障 需要有一个远程…

软件设计师15--进程资源图

软件设计师15--进程资源图 考点1&#xff1a;进程资源图例题&#xff1a; 考点1&#xff1a;进程资源图 例题&#xff1a; 1、在如下所示的进程资源图中&#xff0c;D&#xff09;。 A、P1、P2、P3都是非阻塞节点&#xff0c;该图可以化简&#xff0c;所以是非死锁的 B、P1、…
最新文章