如何将图片用二进制格式读取高手请进

如何将图片用二进制格式读取高手请进,第1张

FILE

pFile=fopen("C:\\aaabmp","rb");

BYTE

buf[MAXLEN]={0};//MAXLEN大小自己确定,也可以动态分配

int

Size=fread(buf,1,MAXLEN,pFile);

//size是实际读取的大小,buf中就是存的你想要的数据了

1概述

从netty 4开始,netty加入了内存池管理,采用内存池管理比普通的new ByteBuf性能提高了数十倍。首先介绍PoolChunk

2原理

PoolChunk主要负责内存块的分配与回收,首先来看看两个重要的术语。

上图中是一个默认大小的chunk, 由2048个page组成了一个chunk,一个page的大小为8192, chunk之上有11层节点,最后一层节点数与page数量相等。每次内存分配需要保证内存的连续性,这样才能简单的 *** 作分配到的内存,因此这里构造了一颗完整的平衡二叉树,所有子节点的管理的内存也属于其父节点。如果我们想获取一个8K的内存,则只需在第11层找一个可用节点即可,而如果我们需要16K的数据,则需要在第10层找一个可用节点。如果一个节点存在一个已经被分配的子节点,则该节点不能被分配,例如我们需要16K内存,这个时候id为2048的节点已经被分配,id为2049的节点未分配,就不能直接分配1024这个节点,因为这个节点下的内存只有8K了。

通过上面这个树结构,我们可以看到每次内存分配都是8K(2^n), 比如需要24K内存时,实际上会申请到一块32K的内存。为了分配一个大小为chunkSize/(2^k)的内存段,需要在深度为k的层从左开始查找可用节点。如想分配16K的内存,chunkSize = 16M, 则k=10, 需要从第10层找一个空闲的节点分配内存。那如何快速分配到指定内存呢,netty使用memoryMap记录分配情况。

初始化中memoryMap中key是上图中节点值,value是该节点所在层数,对于节点512,其层数是9,则:

下面看看如何向PoolChunk申请一段内存:

当需要分配的内存大于pageSize时,使用allocateRun实现内存分配。否则使用allocateSubpage分配内存,主要是将一个page分割成多个element进行分配。其中针对请求的大小进行标准化处理(normCapacity是处理后的值),在分配内存是根据使用者请求的内存大小进行计算,匹配最接近的内存单元。在计算时分如下几种情况:

接下来看看allocateRun是如何实现的:

在allocateNode中遍历匹配:

如节点2048被分配出去,更新如下:

References

1 >

分布式系统架构中,分布式事务问题是一个绕不过去的挑战。而微服务架构的流行,让分布式事问题日益突出!

下面我们以电商购物支付流程中,在各大参与者系统中可能会遇到分布式事务问题的场景进行详细的分析!

如上图所示,假设三大参与平台(电商平台、支付平台、银行)的系统都做了分布式系统架构拆分,按上数中的流程步骤进行分析:

1、电商平台中创建订单:预留库存、预扣减积分、锁定优惠券,此时电商平台内各服务间会有分布式事务问题,因为此时已经要跨多个内部服务修改数据;

2、支付平台中创建支付订单(选yhk支付):查询账户、查询限制规则,符合条件的就创建支付订单并跳转银行,此时不会有分布式事务问题,因为还不会跨服务改数据;

3、银行平台中创建交易订单:查找账户、创建交易记录、判断账户余额并扣款、增加积分、通知支付平台,此时也会有分布式事务问题(如果是服务化架构的话);

4、支付平台收到银行扣款结果:更改订单状态、给账户加款、给积分帐户增加积分、生成会计分录、通知电商平台等,此时也会有分布式事务问题;

5、电商平台收到支付平台的支付结果:更改订单状态、扣减库存、扣减积分、使用优惠券、增加消费积分等,系统内部各服务间调用也会遇到分布式事问题;

如上图,支付平台收到银行扣款结果后的内部处理流程:

1、支付平台的支付网关对银行通知结果进行校验,然后调用支付订单服务执行支付订单处理;

2、支付订单服务根据银行扣款结果更改支付订单状态;

3、调用资金账户服务给电商平台的商户账户加款(实际过程中可能还会有各种的成本计费;如果是余额支付,还可能是同时从用户账户扣款,给商户账户加款);

4、调用积分服务给用户积分账户增加积分;

5、调用会计服务向会计(财务)系统写进交易原始凭证生成会计分录;

6、调用通知服务将支付处理结果通知电商平台;

如上图,把支付系统中的银行扣款成功回调处理流程提取出来,对应的分布式事务问题的代码场景:

/ 支付订单处理 /

@Transactional(rollbackFor = Exceptionclass)

public void completeOrder() {

orderDaoupdate();  // 订单服务本地更新订单状态

accountServiceupdate();  // 调用资金账户服务给资金帐户加款

pointServiceupdate();  // 调用积分服务给积分帐户增加积分

accountingServiceinsert();  // 调用会计服务向会计系统写入会计原始凭证

merchantNotifyServicenotify();  // 调用商户通知服务向商户发送支付结果通知

}

本地事务控制还可行吗?

以上分布式事务问题,需要多种分布式事务解决方案来进行处理。

订单处理:本地事务

资金账户加款、积分账户增加积分:TCC型事务(或两阶段提交型事务),实时性要求比较高,数据必须可靠。

会计记账:异步确保型事务(基于可靠消息的最终一致性,可以异步,但数据绝对不能丢,而且一定要记账成功)

商户通知:最大努力通知型事务(按规律进行通知,不保证数据一定能通知成功,但会提供可查询 *** 作接口进行核对)

import javaioBufferedReader;

import javaioFile;

import javaioFileReader;

import javaioRandomAccessFile;

import javanioByteBuffer;

import javanioMappedByteBuffer;

import javaniochannelsFileChannel;

public class ReadBig {

public static String fff = "C:\\mq\\read\\fromxml";

public static void main1(String[] args) throws Exception {

final int BUFFER_SIZE = 0x300000;// 缓冲区大小为3M

File f = new File(fff);

/

map(FileChannelMapMode mode,long position, long size)

mode - 根据是按只读、读取/写入或专用(写入时拷贝)来映射文件,分别为 FileChannelMapMode 类中所定义的

READ_ONLY、READ_WRITE 或 PRIVATE 之一

position - 文件中的位置,映射区域从此位置开始;必须为非负数

size - 要映射的区域大小;必须为非负数且不大于 IntegerMAX_VALUE

所以若想读取文件后半部分内容,如例子所写;若想读取文本后1/8内容,需要这样写map(FileChannelMapModeREAD_ONLY,

flength()7/8,flength()/8)

想读取文件所有内容,需要这样写map(FileChannelMapModeREAD_ONLY, 0,flength())

/

MappedByteBuffer inputBuffer = new RandomAccessFile(f, "r")

getChannel()map(FileChannelMapModeREAD_ONLY,

flength() / 2, flength() / 2);

byte[] dst = new byte[BUFFER_SIZE];// 每次读出3M的内容

long start = SystemcurrentTimeMillis();

for (int offset = 0; offset < inputBuffercapacity(); offset += BUFFER_SIZE) {

if (inputBuffercapacity() - offset >= BUFFER_SIZE) {

for (int i = 0; i < BUFFER_SIZE; i++)

dst[i] = inputBufferget(offset + i);

} else {

for (int i = 0; i < inputBuffercapacity() - offset; i++)

dst[i] = inputBufferget(offset + i);

}

int length = (inputBuffercapacity() % BUFFER_SIZE == 0) BUFFER_SIZE

: inputBuffercapacity() % BUFFER_SIZE;

Systemoutprintln(new String(dst, 0, length));// new

// String(dst,0,length)这样可以取出缓存保存的字符串,可以对其进行 *** 作

}

long end = SystemcurrentTimeMillis();

Systemoutprintln("读取文件文件一半内容花费:" + (end - start) + "毫秒");

}

public static void main2(String[] args) throws Exception {

int bufSize = 1024;

byte[] bs = new byte[bufSize];

ByteBuffer byteBuf = ByteBufferallocate(1024);

FileChannel channel = new RandomAccessFile(fff, "r")getChannel();

while (channelread(byteBuf) != -1) {

int size = byteBufposition();

byteBufrewind();

byteBufget(bs); // 把文件当字符串处理,直接打印做为一个例子。

Systemoutprint(new String(bs, 0, size));

byteBufclear();

}

}

public static void main(String[] args) throws Exception {

BufferedReader br = new BufferedReader(new FileReader(fff));

String line = null;

while ((line = brreadLine()) != null) {

Systemoutprintln(line);

}

}

}

Netty在实现 ByteBuf 时采用了引用计数法进行 ByteBuf 的回收,使用引用计数法进行回收的 ByteBuf 都扩展了 AbstractReferenceCountedByteBuf 类,在使用 AbstractReferenceCountedByteBuf 时需要调用 AbstractReferenceCountedByteBufretain 方法递增引用计数器,在使用完毕时则需要调用 AbstractReferenceCountedByteBufrelease 方法递减引用计数器,当计数器为 0 时,会进行 ByteBuf 的回收工作:池化的 ByteBuf 不会进行实际的内存释放,会将占用的内存归还给内存池,非池化的 ByteBuf 则会直接释放内存(为了叙述简单,后面释放内存则指真正释放内存或者将内存归还给内存池)。

通过上面的描述可知, ByteBuf 的正确回收依赖 retain 和 release 方法的正确调用,内存提前释放(即在使用 ByteBuf 时没有调用 retain 方法,导致提前释放)应用会报错,用户也能及时感知到;但是如果使用完 ByteBuf 忘了调用 release 则会导致内存不能及时得到回收,造成内存泄漏,且内存泄漏用户无法及时感知,久而久之就会发生OOM。为了解决这种问题,Netty采用了内存泄漏检测机制,发生内存泄漏时会通过日志将内存泄漏信息打印出来,报告给用户。

Netty的内存泄漏检测使用了 WeakReference ,即弱引用,了解过Java四种引用类型(强、软、弱、虚)和引用队列( ReferenceQueue )的读者知道,弱引用持有的对象会在虚拟机触发GC时(不管回收之后内存是否够用)被回收掉,如果使用具有引用队列参数的构造函数实例化 WeakReference 时,弱引用持有的对象在GC被回收时,弱引用自身会被放入引用队列。

为了后面能更好的理解Netty内存泄漏检测的细节,下面先看几个弱引用的例子,在下面的几个例子中,我们使用的数据类和自定义的弱引用类子类如下:

好了,三个例子已经介绍完毕,后面在介绍Netty内存泄漏检测时就使用了这里的例子结果,在具体介绍时会和这里的例子一一对应。

Netty中将普通 ByteBuf 转为具有内存泄漏检测功能的 ByteBuf 是通过 AbstractByteBufAllocatortoLeakAwareBuffer 方法实现的,我们直接在Eclipse中看该方法的调用层次即可知道Netty在哪里对 ByteBuf 进行了转换,该方法调用如下图所示:

可见池化内存分配器在分配heap或者direct ByteBuf 时都进行了转换,非池化内存分配器仅在分配direct ByteBuf 时进行了转换。个人理解时采用池化内存需要特别关注内存释放,否则为了实现池化内存预先分配的一大块内存会因为没有释放被很快分配完,造成后面没有内存进行分配。非池化分配的直接内存也需要特别注意释放,放置内存泄漏;非池化分配的heap内存(其实就是一个 byte 数组)则可以在对象被回收时同时被回收掉,发生内存泄漏的可能性较小。

本节介绍Netty中内存泄漏检测相关的类,仅做一个大致介绍,类中的重要方法我们放在后面介绍。

主要负责使用 track 方法对指定的 ByteBuf 进行内存检测泄漏进行追踪,并返回负责追踪的 ResourceLeakTracker 类实例,同时在调用 track 方法时,也会根据指定的检测级别汇报最近的内存泄漏检测结果。该类由工厂类 ResourceLeakDetectorFactory 负责实例化,默认的实现为 ResourceLeakDetector ,在 ResourceLeakDetectorFactory 类的默认实现 DefaultResourceLeakDetectorFactory 中,也会根据用户是否配置了 ionettycustomResourceLeakDetector 来决定采用默认实现 ResourceLeakDetector 还是使用用户自定义的 ResourceLeakDetector ,用户自定义的 ResourceLeakDetector 必须是其子类。

默认实现为 DefaultResourceLeak , DefaultResourceLeak 实现了 ResourceLeakTracker 和 ResourceLeak 接口,同时也继承了类 WeakReference ,是一个弱引用实现。首先,同上面 例2 的结果一样,如果在使用 ByteBuf 时忘了调用 AbstractReferenceCountedByteBufrelease 方法,那么将不会调用 DefaultResourceLeakclear 方法去手动清空该弱引用持有的实际对象,在发生GC时,会由垃圾收集器对弱引用持有的实际对象进行回收,即发生了内存泄漏,同时该弱引用自身也会被加入到引用队列中,该引用队列是 ResourceLeakDetector 的成员域,上面介绍 ResourceLeakDetector 类时说到该类会在用户 track 指定 ByteBuf 是汇报检测结果,该类的汇报数据来源就是引用队列。 DefaultResourceLeak 同时还提供了 record 方法可以让用户在指定时机选择调用,这个方法可以记录用户的调用轨迹(堆栈)。 Record 同时也是一种单链表,在 DefaultResourceLeak 中就使用单链表记录用户的调用轨迹。

DefaultResourceLeak 供用户记录程序调用轨迹的类,也就是 DefaultResourceLeakrecord 方法返回的对象,继承自 Throwable ,因此可以使用 ThrowablegetStackTrace 方法获得调用轨迹信息,打印在内存泄漏报告中可以让用户更好的排除内存泄漏问题。

在上面介绍 ResourceLeakTracker 时,说到其默认实现为 DefaultResourceLeak , DefaultResourceLeak 提供了 record 方法记录用户的调用轨迹,用户可在调用 ByteBuf 方法时调用 record 方法记录调用轨迹,调用的频率越多,后面在汇报内存泄漏情况时就能打印出越详细的信息,这样也能更方便的排查问题。

Netty提供了两个 ByteBuf 的封装类供选择,就对应不同的 record 调用频率,每个封装类都持有 ResourceLeakTracker 对象,Netty根据配置的内存检测级别(下一节介绍相关配置参数)使用不同的 ByteBuf 封装类。

Netty提供的两个 ByteBuf 封装类就是 SimpleLeakAwareCompositeByteBuf 和 AdvancedLeakAwareCompositeByteBuf , AdvancedLeakAwareCompositeByteBuf 是 SimpleLeakAwareCompositeByteBuf 的子类, SimpleLeakAwareCompositeByteBuf 类仅仅持有 ResourceLeakTracker 对象,但是看其源码,发现没有调用过 record 方法,所以只能知道是否发生了内存泄漏时,无法打印出任何调用轨迹信息。 AdvancedLeakAwareCompositeByteBuf 作为 SimpleLeakAwareCompositeByteBuf 的子类,在 ByteBuf 的多个方法中调用了 record 方法,所以在发生内存泄漏时,能够打印出比较详细的调用轨迹信息。

在 AdvancedLeakAwareCompositeByteBuf 类中使用了配置参数 ionettyleakDetectionacquireAndReleaseOnly 来控制是否只是在调用增加或减少引用计数器的方法时才调用 record 方法记录调用轨迹,默认为false。 AdvancedLeakAwareCompositeByteBuf 中 retain 和 release 方法因为改变了引用计数器就直接调用了 record 方法,而该类中的其他方法则根据 ionettyleakDetectionacquireAndReleaseOnly 的配置决定是否调用 record 方法,这里为了节省篇幅就不列出 AdvancedLeakAwareCompositeByteBuf 类中调用 record 的方法了,读者可自行查看。

在介绍相关配置参数之前,我们先看下Netty提供的内存泄漏检测级别:

LevelADVANCED 和 LevelPARANOID 使用的 ByteBuf 包装类都是 AdvancedLeakAwareCompositeByteBuf ,我们上面介绍 ResourceLeakDetector 类时提到该类使用 track 方法对指定的 ByteBuf 进行内存检测泄漏进行追踪,并返回负责追踪的 ResourceLeakTracker 类实例,同时在调用 track 方法时,也会根据指定的检测级别汇报最近的内存泄漏检测结果。如果内存泄漏检测级别为 LevelPARANOID 时则每次调用 track 方法都会进行内存泄漏报告;如果级别为 LevelADVANCED 或者 LevelSIMPLE 则会以一定频率进行内存泄漏报告,而不是每次 track 都进行报告。

是否关闭Netty内存泄漏检测功能,默认为false。如果该参数配置为false,则默认的内存泄漏检测级别根据此参数的配置为 LevelDISABLED ,否则默认的级别为 LevelSIMPLE 。

配置内存泄漏检测级别的参数,用于老版本的配置参数。

新的内存泄漏检测级别参数,如果没有配置,则会采用老版本参数配置的级别作为最终配置。

在第4节介绍内存泄漏检测相关类时,我们介绍过 DefaultResourceLeak 提供了 record 方法记录用户的调用轨迹,如果当前保存的调用轨迹记录数 Record 大于参数 ionettyleakDetectiontargetRecords 配置的值,那么会以一定的概率(1/2^n)删除头结点之后再加入新的记录,当然也有可能不删除头结点直接新增新的记录。

该参数的默认为4。

上面介绍过,在 AdvancedLeakAwareCompositeByteBuf 类中使用了配置参数 ionettyleakDetectionacquireAndReleaseOnly 来控制是否只是在调用增加或减少引用计数器的方法时才调用 record 方法记录调用轨迹,默认为false。

在介绍 ResourceLeakDetector 类时提到过,默认的 ResourceLeakDetector 类就是 ResourceLeakDetector ,但是用户可以使用参数 ionettycustomResourceLeakDetector 来决定采用默认实现 ResourceLeakDetector 还是使用用户自定义的 ResourceLeakDetector 。

我们在第二节介绍了Netty中将普通 ByteBuf 转为具有内存泄漏检测功能的 ByteBuf 是通过 AbstractByteBufAllocatortoLeakAwareBuffer 方法实现的。

这里我们先看下该方法的源码:

上面的源码中是调用 AbstractByteBufleakDetectortrack(buf) 返回 ResourceLeakTracker 类对象的,这里我们看下默认的 ResourceLeakDetector 中 track 方法实现:

我们看到 AbstractByteBufAllocatortoLeakAwareBuffer 对 ResourceLeakDetectortrack 返回的 DefaultResourceLeak 和传入的 ByteBuf 对象进行封装,返回了具有内存泄漏检测功能的 ByteBuf 封装类 SimpleLeakAwareCompositeByteBuf 或其子类 AdvancedLeakAwareCompositeByteBuf 。如果应用程序在使用 ByteBuf 正确调用了 retain 和 release 方法,则在引用计数器为0时,则会清除弱引用持有的实际对象,发生GC时, DefaultResourceLeak 也不会被放入引用队列中(见前面第2节 例3 结果)。

但是如果应用程序在使用 ByteBuf 没有正确调用 retain 和 release 方法,则不会清除弱引用持有的实际对象,此时如果实际上已经没有强引用指向该 ByteBuf ,那么在发生GC时,垃圾收集器会回收该 ByteBuf ,而弱引用 DefaultResourceLeak 会被放入引用队列中(见前面第2节 例2 结果),加入到引用队列中的就是识别到的发生内存泄漏的 ByteBuf 。在 ResourceLeakDetectortrack 方法中调用的 reportLeak 输出的就是引用队列中的弱引用 DefaultResourceLeak :

到这里,已经基本上介绍完Netty内存检测的实现原理,下面我们再看下 DefaultResourceLeakrecord 是如何记录调用轨迹的:

最后我们再看下 Record 是如何输出调用轨迹的,前面我们说到 Record 继承自类 Throwable ,因此可使用 getStackTrace 方法获取实例化该对象时的调用轨迹,所以上面在输出内存泄漏报告时就调用了 RecordtoString 方法:

以上就是关于如何将图片用二进制格式读取高手请进全部的内容,包括:如何将图片用二进制格式读取高手请进、Netty内存模型-PoolChunk、微服务架构的分布式事务问题如何处理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9316460.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-27
下一篇2023-04-27

发表评论

登录后才能评论

评论列表(0条)

    保存