安卓MediaRecorder(2)录制源码分析
文章目录
- 前言
- JAVA new MediaRecorder() 源码分析
- android_media_MediaRecorder.cpp native_init()
- MediaRecorder.java postEventFromNative
- android_media_MediaRecorder.cpp native_setup()
- MediaRecorder 参数设置
- MediaRecorder.prepare 分析
- MediaRecorder.start 分析
- MediaRecorder.stop 分析
- 结语
本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134634628
最新更新地址 https://gitee.com/chenjim/chenjimblog
前言
通过前文 安卓MediaRecorder(1)录制音频的详细使用,我们已经知道如何使用。
本文主要分析一下 Framework 中相关流程。
下图是谷歌提供的MediaRecorder状态关系图

JAVA new MediaRecorder() 源码分析
public class MediaRecorder implements AudioRouting,AudioRecordingMonitor,AudioRecordingMonitorClient,MicrophoneDirection {static {// 静态代码块,加载链接 liblibmedia_jni.soSystem.loadLibrary("media_jni");native_init();}// 已经废弃 public MediaRecorder() {// 传入 APP Contextthis(ActivityThread.currentApplication());}public MediaRecorder(@NonNull Context context) {// 要求 Context 不为空Objects.requireNonNull(context);// 创建EventHandler,主要用于JNI层回调时切换到当前App端线程中Looper looper;if ((looper = Looper.myLooper()) != null) {// 如果当前线程有Looper,就使用当前线程的LoopermEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {// 使用主线程的 Looper,Jni回调信息会在主线程执行 mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}// 录制音频的声道数,此处默认 1 (mono即单声道),2 (stereo即双声道立体声),可以通过 setAudioChannels 修改mChannelCount = 1;// 创建弱引用,并初始化try (ScopedParcelState attributionSourceState = context.getAttributionSource().asScopedParcelState()) {native_setup(new WeakReference<>(this), ActivityThread.currentPackageName(),attributionSourceState.getParcel());}}
android_media_MediaRecorder.cpp native_init()
获取JAVA层 android.media.MediaRecorder 对象
并将 JAVA 对象的属性 mNativeContext、mSurface、postEventFromNative 保存在 fields
详细代码如下
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{jclass clazz;clazz = env->FindClass("android/media/MediaRecorder");if (clazz == NULL) {return;}fields.context = env->GetFieldID(clazz, "mNativeContext", "J");if (fields.context == NULL) {return;}fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");if (fields.surface == NULL) {return;}jclass surface = env->FindClass("android/view/Surface");if (surface == NULL) {return;}fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.post_event == NULL) {return;}clazz = env->FindClass("java/util/ArrayList");if (clazz == NULL) {return;}gArrayListFields.add = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z");gArrayListFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
MediaRecorder.java postEventFromNative
这个是Jni消息回来的接口,最终会发到 MediaRecorder.EventHandler 的 handleMessage 中
进而可以通过 MediaRecorder.OnInfoListener 、MediaRecorder.OnErrorListener、
AudioRouting.OnRoutingChangedListener 回调到 APP
对应源码如下
private static void postEventFromNative(Object mediarecorder_ref,int what, int arg1, int arg2, Object obj) {MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get();if (mr == null) {return;}if (mr.mEventHandler != null) {Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj);mr.mEventHandler.sendMessage(m);}
}// EventHandler 如下
public class MediaRecorder {...private class EventHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch(msg.what) {case MEDIA_RECORDER_EVENT_ERROR:case MEDIA_RECORDER_TRACK_EVENT_ERROR:if (mOnErrorListener != null)mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2);return;case MEDIA_RECORDER_EVENT_INFO:case MEDIA_RECORDER_TRACK_EVENT_INFO:if (mOnInfoListener != null)mOnInfoListener.onInfo(mMediaRecorder, msg.arg1, msg.arg2);return;case MEDIA_RECORDER_AUDIO_ROUTING_CHANGED:// 耳机使能的消息 return;}}}
android_media_MediaRecorder.cpp native_setup()
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,jstring packageName, jobject jAttributionSource)
{// 构建 JNI 对象 MediaRecorder,attributionSource 可以认为是 JNI 中的上下文 Context sp<MediaRecorder> mr = new MediaRecorder(attributionSource);...// 创建 JNI JNIMediaRecorderListener , 其收到的消息最终通过 fields.post_event 回到JAVAsp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this);mr->setListener(listener);...// 传递客户端包名,以进行权限跟踪 mr->setClientName(clientName);// 将创建的 mr 保存到 fields.context,也就是 Java 层 MediaRecorder 中的 mNativeContext setMediaRecorder(env, thiz, mr);
}
MediaRecorder JNI 构造
// frameworks/av/media/libmedia/mediarecorder.cpp
MediaRecorder::MediaRecorder(const AttributionSourceState &attributionSource): mSurfaceMediaSource(NULL)
{// 通过 binder 获取 MediaPlayerService const sp<IMediaPlayerService> service(getMediaPlayerService());if (service != NULL) {// 通过 MediaPlayerService 创建 MediaRecorderClient mMediaRecorder = service->createMediaRecorder(attributionSource);}...
}
MediaRecorder 类关系如下
class MediaRecorder : public BnMediaRecorderClient, public virtual IMediaDeathNotifier {...}
class BnMediaRecorderClient: public BnInterface<IMediaRecorderClient> {...}
MediaPlayerService 类关系如下
class MediaPlayerService : public BnMediaPlayerService {...}
class BnMediaPlayerService: public BnInterface<IMediaPlayerService> {...}
MediaPlayerService 中 createMediaRecorder 如下
// frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const AttributionSourceState& attributionSource)
{...sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, verifiedAttributionSource);...return recorder;
}
MediaRecorderClient 构造如下
// frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service,const AttributionSourceState& attributionSource)
{// 构造StagefrightRecorder,mRecorder = new StagefrightRecorder(attributionSource);mMediaPlayerService = service;
}StagefrightRecorder 类继承关系如下
struct StagefrightRecorder : public MediaRecorderBase {...}
到此 JAVA 层 new MediaRecord() 相关源码已经分析完成
MediaRecorder 参数设置
Java层 setAudioSource 、 setOutputFormat 等均调用了 Native 接口,下面以 setAudioSource 为例
// frameworks/base/media/java/android/media/MediaRecorder.java
public native void setAudioSource(@Source int audioSource) throws IllegalStateException;
// frameworks/base/media/jni/android_media_MediaRecorder.cpp
static void android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as)
{...// 读取初始化时保存的mrsp<MediaRecorder> mr = getMediaRecorder(env, thiz);if (mr == NULL) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed.");
}// frameworks/av/media/libmedia/mediarecorder.cpp
status_t MediaRecorder::setVideoSource(int vs)
{...// 上面知道,这里的 mMediaRecorder 是 MediaRecorderClient status_t ret = mMediaRecorder->setVideoSource(vs);return ret;
}// frameworks/av/media/libmediaplayerservice/MediaRecorderClient.cpp
status_t MediaRecorderClient::setVideoSource(int vs)
{...// 上面知道,这里的 mRecorder 是 StagefrightRecorder return mRecorder->setVideoSource((video_source)vs);
}// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::setVideoSource(video_source vs) {...// 最终数据保存在 mVideoSource 中 if (vs == VIDEO_SOURCE_DEFAULT) {mVideoSource = VIDEO_SOURCE_CAMERA;} else {mVideoSource = vs;}return OK;
}
同理,其它参数设置多数最终都是保存在 StagefrightRecorder 中,录制相关的流程很多也是在其中控制
本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134634628
MediaRecorder.prepare 分析
依据前面的分析,最终 prepare 真正实现如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::prepareInternal() {...status_t status = OK;// 依据不同的输出格式,执行不同的 setup switch (mOutputFormat) {case OUTPUT_FORMAT_DEFAULT:case OUTPUT_FORMAT_THREE_GPP:case OUTPUT_FORMAT_MPEG_4:case OUTPUT_FORMAT_WEBM:status = setupMPEG4orWEBMRecording();break;case OUTPUT_FORMAT_AMR_NB:case OUTPUT_FORMAT_AMR_WB:status = setupAMRRecording();break;case OUTPUT_FORMAT_AAC_ADIF:case OUTPUT_FORMAT_AAC_ADTS:status = setupAACRecording();break;case OUTPUT_FORMAT_RTP_AVP:status = setupRTPRecording();break;case OUTPUT_FORMAT_MPEG2TS:status = setupMPEG2TSRecording();break;case OUTPUT_FORMAT_OGG:status = setupOggRecording();break;default:ALOGE("Unsupported output file format: %d", mOutputFormat);status = UNKNOWN_ERROR;break;}return status;
}
这里我们分析一下录制 MP4 的 prepare 流程 setupMPEG4orWEBMRecording
status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {// 先清理 MediaWriter mWriter.clear();mTotalBitRate = 0;status_t err = OK;sp<MediaWriter> writer;sp<MPEG4Writer> mp4writer;if (mOutputFormat == OUTPUT_FORMAT_WEBM) {writer = new WebmWriter(mOutputFd);} else {// 我们这里分析 MP4 录制,MPEG4Writer 主要是用来写入编码后的音视频内容 writer = mp4writer = new MPEG4Writer(mOutputFd);}if (mVideoSource < VIDEO_SOURCE_LIST_END) {// 如果编码器未配置,设置默认的编码器 setDefaultVideoEncoderIfNecessary();sp<MediaSource> mediaSource;// 设置 视频源err = setupMediaSource(&mediaSource);if (err != OK) {return err;}sp<MediaCodecSource> encoder;// 编码参数配置,然后通过 MediaCodecSource::Create 创建编码器err = setupVideoEncoder(mediaSource, &encoder);if (err != OK) {return err;}// MPEG4Writer 添加编码通道,一般会有audio、video 两个,这里是 videowriter->addSource(encoder);mVideoEncoderSource = encoder;// 输出文件的码率,是视频和音频总码率之和mTotalBitRate += mVideoBitRate;}// Audio source is added at the end if it exists.// This help make sure that the "recoding" sound is suppressed for// camcorder applications in the recorded files.// disable audio for time lapse recordingconst bool disableAudio = mCaptureFpsEnable && mCaptureFps < mFrameRate;if (!disableAudio && mAudioSource != AUDIO_SOURCE_CNT) {// 通过 createAudioSource() 配置音频编码参数,进而通过 MediaCodecSource::Create 创建 编码器err = setupAudioEncoder(writer);if (err != OK) return err;mTotalBitRate += mAudioBitRate;}...// 监听 MPEG4Writer 一些参数的回调 writer->setListener(mListener);mWriter = writer;return OK;
}
通过如上,可以看到 MediaRecorder.prepare 主要是进行参数的配置、编码器的初始化
MediaRecorder.start 分析
依据前面的分析,最终 start 真正实现如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::start() {Mutex::Autolock autolock(mLock);if (mOutputFd < 0) {ALOGE("Output file descriptor is invalid");return INVALID_OPERATION;}status_t status = OK;if (mVideoSource != VIDEO_SOURCE_SURFACE) {status = prepareInternal();if (status != OK) {return status;}}switch (mOutputFormat) {case OUTPUT_FORMAT_DEFAULT:case OUTPUT_FORMAT_THREE_GPP:case OUTPUT_FORMAT_MPEG_4:case OUTPUT_FORMAT_WEBM:{sp<MetaData> meta = new MetaData;// 设置 meta 信息setupMPEG4orWEBMMetaData(&meta);// MPEG4Writer 传入 meta 信息,// startWriterThread 开启写入线程 // setupAndStartLooper 启动 ALooper, mReflector 用于信息传递 // 通过 writeFtypBox(MetaData *param) 写入// startTracks(MetaData *params) 启动音、视频Track,参见 MPEG4Writer::Track::startstatus = mWriter->start(meta.get());break;}...}if (status != OK) {// start 异常 mWriter.clear();mWriter = NULL;}if ((status == OK) && (!mStarted)) {mAnalyticsDirty = true;mStarted = true;...// 用于编码耗电统计 addBatteryData(params);}return status;
}
MediaRecorder.stop 分析
依据前面的分析,最终 stop 真正实现如下
// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::stop() {Mutex::Autolock autolock(mLock);status_t err = OK;if (mCaptureFpsEnable && mCameraSourceTimeLapse != NULL) {// 延时录制,详细可参见 CameraSourceTimeLapse.cppmCameraSourceTimeLapse->startQuickReadReturns();mCameraSourceTimeLapse = NULL;}int64_t stopTimeUs = systemTime() / 1000;for (const auto &source : { mAudioEncoderSource, mVideoEncoderSource }) {// 设置停止时间戳if (source != nullptr && OK != source->setStopTimeUs(stopTimeUs)) {}}if (mWriter != NULL) {// MPEG4Writer 的停止 ,实际调用其 reset(true, true)// stopWriterThread() 停止写的线程// Track 停止// release 关闭文件,停止释放Looper,资源状态重置err = mWriter->stop();mLastSeqNo = mWriter->getSequenceNum();mWriter.clear();}// 写入参数相关信息flushAndResetMetrics(true);// 重置参数状态mDurationRecordedUs = 0;mDurationPausedUs = 0;mNPauses = 0;mTotalPausedDurationUs = 0;mPauseStartTimeUs = 0;mStartedRecordingUs = 0;mGraphicBufferProducer.clear();mPersistentSurface.clear();mAudioEncoderSource.clear();mVideoEncoderSource.clear();......return err;
}
结语
到这里,已经完成了 MediaRecorder 录制 Framework 源码的分析。
其它部分流程,可以对照参见 StagefrightRecorder.cpp 中源码。希望对你有所帮助。
如果你在使用MediaRecorder的过程中遇到了其他问题,欢迎留言讨论。
如果你觉得本文还不错,可以点赞+收藏。
相关文章
安卓MediaRecorder(1)录制音频的详细使用
安卓MediaRecorder(2)录制源码分析
安卓MediaRecorder(3)音频采集编码写入详细源码分析
安卓MediaRecorder(4)视频采集编码写入详细源码分析
相关文章:
安卓MediaRecorder(2)录制源码分析
文章目录 前言JAVA new MediaRecorder() 源码分析android_media_MediaRecorder.cpp native_init()MediaRecorder.java postEventFromNativeandroid_media_MediaRecorder.cpp native_setup() MediaRecorder 参数设置MediaRecorder.prepare 分析MediaRecorder.start 分析MediaRec…...
MySql数据库全量备份脚本
#!/bin/bash# 设置数据库连接信息 DB_HOST"localhost" DB_USER"root" DB_PASS"密码" DB_NAMES("db1" "db2" "db3" "db4")# 设置备份目录 BACKUP_DIR"/home/mysql/mysql-back/everyday" # 每天…...
windows10下jdk安装
文章目录 windows10下jdk安装说明what安装包下载执行安装包验证是否安装成功 windows10下jdk安装 说明 操作系统:windows10 版本:1.8 what JDK(Java Development Kit) 是 Java 语言的软件开发工具包 安装包下载 https://www.oracle.com/java/techn…...
Centos7防火墙及端口开启
1、防火墙 1.1、查看防火墙是否开启 systemctl status firewalld 1.2、开启防火墙 firewall-cmd --list-ports 1.3、重启防火墙 firewall-cmd --reload 2、端口 2.1、查看所有已开启的端口号 firewall-cmd --list-ports 2.2、手动开启端口 启动防火墙后,默认没有开…...
vue开发,axios网络请求框架基本用法和封装
axios安装 npm install axiosaxios基本用法 默认的get请求,参数用params追加,多个参数通过json对象的方式,例如params:‘{type:“home”,page:1}’ axios({url: https://api.videolog.net.cn/baidu/token,params: }).then(value > {co…...
对比SPI、UART、I2C通信的区别与应用
SPI、UART、I2C通信是常用的数字通信协议,它们在不同的场景下有不同的应用。下面,我将分别介绍它们的特点、区别与应用。 SPI通信 SPI通信是一种串行同步通信协议,它的全称为“Serial Peripheral Interface”。SPI通信是一种单主多从的通信方…...
CentOS7安装MySQL8.0
一、使用Yum安装 1. 使用wget下载MySQL的rpm包 wget https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm2. 安装下载好的rpm包 yum localinstall mysql80-community-release-el7-3.noarch.rpm 3. 安装mysql(该步可能出现问题) yum…...
【Go<—>Java】gRPC测试注意事项
在做go和Java之间gRPC调用之前需要完成以下两项工作: go语言版本的gRPC调用,实现server端和client端Java语言版本的gRPC调用,实现server端和client端 由于gRPC是跨语言的通信协议,所以我们可以相互调用,有以下2种调用…...
java面试题整合
1.Java数据类型 ✅ Java是一种静态类型语言,它具有丰富的数据类型用于声明变量和方法返回类型。Java中的数据类型分为两类:原始数据类型(Primitive Data Types)和引用数据类型(Reference Data Types)。 原…...
2023年12月7日:QT实现登陆界面
#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口设置this->resize(600,500);//重新设置窗口大小this->setWindowTitle("QQ-盗版");//设置窗口名为QQ-盗版this->setWindowIcon(QIcon("D:\\Qt\\funny\\pi…...
常用的测试用例大全
登录、添加、删除、查询模块是我们经常遇到的,这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求,密码不符合要求(格式上的要求) ④ 密码符合要求…...
《python每天一小段》--12 数据可视化《1》
欢迎阅读《Python每天一小段》系列!在本篇中,将使用Python Matplotlib实现数据可视化的简单图形。 文章目录 一、概念(1)安装matplotlib(2)数据可视化实现步骤 二、绘制简单的折线图(1ÿ…...
分类预测 | Matlab实现HPO-GRU【23年新算法】基于猎食者优化算法优化门控循环单元的数据分类预测
分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机的数据分类预测【23年新算法】 目录 分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机的数据分类预测【23年新算法】分类效果基本描述程序设计参考资料 分类效果 基本描述 1.HPO-GRU【23年新算法】基于猎食者优化算法优…...
【Pytorch】学习记录分享2——Tensor基础,数据类型,及其多种创建方式
pytorch 官方文档 Tensor基础,数据类型,及其多种创建方式 1. 创建 Creating Tensor: 标量、向量、矩阵、tensor2. 三种方法可以创建张量,一是通过列表(list),二是通过元组(tuple),三是通过Numpy的数组(arra…...
实验7:索引和视图定义
【实验目的】 1、了解索引和视图的含义 2、熟悉索引和视图的创建规则 3、掌握索引和视图的创建和管理 【实验设备及器材】 1、硬件:PC机; 2、软件:(1)Windows7; (2)Microsoft SQL Server 2012。 【主要内容】 索引的创建、删除、重建…...
Source Tree回滚 重置 贮藏操作
回滚提交 source tree的回滚提交: 在执行该操作时将会对history中提交的指定节点直接进行回滚,将该节点执行的提交操作撤销(如当前节点是提交文件,执行回滚提交时将会删除该文件,如果当前节点的前面的节点对该节点内容进行修改后,执行回滚提交时需要执行冲突解决),同时生成一次…...
Android13 不能静态注册的几个广播
Android13 不能静态注册的几个广播 文章目录 Android13 不能静态注册的几个广播一、不能静态注册的广播:二、静态注册无法生效的分析1、Intent.java2、其他地方声明了不能静态注册的广播3、为啥静态注册的广播无效?4、其他静态注册无法生效的广播5、其他Android fra…...
吴恩达深度学习L2W1作业1
初始化 欢迎来到“改善深度神经网络”的第一项作业。 训练神经网络需要指定权重的初始值,而一个好的初始化方法将有助于网络学习。 如果你完成了本系列的上一课程,则可能已经按照我们的说明完成了权重初始化。但是,如何为新的神经网络选择…...
uniapp原生插件之安卓app添加到其他应用打开原生插件
插件介绍 安卓app添加到其他应用打开原生插件,接收分享的文本和文件,支持获取和清空剪切板内容 插件地址 安卓app添加到其他应用打开原生插件,支持获取剪切板内容 - DCloud 插件市场 超级福利 uniapp 插件购买超级福利 详细使用文档 u…...
scala编码
1、Scala高级语言 Scala简介 Scala是一门类Java的多范式语言,它整合了面向对象编程和函数式编程的最佳特性。具体来讲Scala运行于Java虚拟机(JVM)之上,井且兼容现有的Java程序,同样具有跨平台、可移植性好、方便的垃圾回收等特性…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
