1 VGGNet介绍 1.1 VGGnNet概述 VGGNet是牛津大学视觉几何组(Visual Geometry Group)提出的模型,故简称VGGNet, 该模型在2014年的ILSVRC中取得了分类任务第二、定位任务第一的优异成绩。该模型证明了增加网络的深度能够在一定程度上影响网络最终的性能。
论文地址:原文链接
根据卷积核大小与卷积层数目不同,VGG可以分为6种子模型,分别是A、A-LRN、B、C、D、E,分别对应的模型为VGG11、VGG11-LRN(第一层采用LRN)、VGG13、VGG16-1、VGG16-3和VGG19。不同的后缀代表不不同的网络层数。VGG16-1表示后三组卷积块中最后一层卷积采用卷积核尺寸为1*1,VGG16-3为3*3。VGG19位后三组每组多一层卷积,VGG19为3*3的卷积。我们常看到的基本是D、E这两种模型,官方给出的6种结构图如下:
1.2 VGG16网络结构 VGG16的网络结果如上图所示:在卷积层1(conv3-64),卷积层2(conv3-128),卷积层3(conv3-256),卷积层4(conv3-512)分别有64个,128个,256个,512个3X3卷积核,在每两层之间有池化层为移动步长为2的2X2池化矩阵(maxpool)。在卷积层5(conv3-512)后有全连接层,再之后是soft-max预测层。
处理过程的直观表示:
1.2.1 输入层 输入的图像一般情况是彩色的三维图像,所以输入的维度是`[B,N,H,W] , B是batchsize的意思, N输入 的Channel,彩色图像是3 , H是高度为224 ,W是宽度为224。
输入的图像经历两个卷积3✖3的卷积, BatchNorm和ReLU,输出的维度是[B,64,224,224]。
这里要注意两点:第一,默认卷积层后保持维度不改变,第二,卷积层、 BN层和激活函数一般会同时存在。这两点已经成了默认的规则。
然后,输入到池化层,维度变为[B,64,112,112]。
1.2.2 第二个卷积Block 到了第二个卷积Block,输入的维度为[[B,64,112,112],输出的Channel变成了128,所以在定义卷积的时 候需要扩大channel。
从上图中,我们可以看到是两个128Channel的卷积,但是这两个卷积还有所不同,在经历第一个卷积的时候,输入的Channel设置为64,输出的Channel设置为128,经过卷积之后维度变 为[B,128,112,112],然后再经过BN层和激活函数层。
第二卷积的输入和输出都是128,然后再经过BN成和激活函数层,在这里维度没有发生变化。然后再输入到MaxPool,将尺寸减小一半,输出的维度变为[B,128,56,56]
1.2.3 第三个卷积Block 这个Block中有3个3✖3的卷积,和上面的Block一样,第一个卷积起到成承上启下的桥梁作用,经过第一个卷积后,维度变为[B,256,56,56],然后经过第二个和第三个卷积,然后再经过最大池化层,维度变为[B,256,28,28]。
1.2.4 第四个卷积Block 同上, Channel为512,所以经过这个Block后,维度变为[B,512,14,14].
1.2.5 第五个卷积Block 同上, Channel为512,所以经过这个Block后,维度变为[B,512,7,7],经过卷积后得到了维度为[B,512,7,7]的特征图。
1.2.6 变换维度 这部分也是一个承上启下的作用,将[B,N,W,H]维度变成[B,N✖W✖H]。方法有很多,一般常用view方法,代码如下:
# forward函数中 x = x.view(x.size(0), -1) 也可以使用flatten方法,代码如下:
# forward函数中 x = torch.flatten(x, 1) 我们观察现在的网络,在变换维度之前还有一部操作。
现在统一使用平均池化,pytorch官方使用的 nn.AdaptiveAvgPool2d ,也可以使用AvgPool2d。关于这两个平均池化的区别可以参考:nn.AdaptiveAvgPool2d和nn.AvgPool2d的区别。
所以上面的代码改为:
#init函数中 self.
大家通常会问,大模型训练为什么一定要用A100,用4090难道不行吗?先说结论,大模型的训练用 4090 是不行的,但推理(inference/serving)用 4090 不仅可行,在性价比上还能比 H100 稍高。4090 如果极致优化,性价比甚至可以达到 H100 的 2 倍。
事实上,H100/A100 和 4090 最大的区别就在通信和内存上,算力差距不大。
H100A1004090Tensor FP16 算力989 Tflops312 Tflops330 TflopsTensor FP32 算力495 Tflops156 Tflops83 Tflops内存容量80 GB80 GB24 GB内存带宽3.35 TB/s2 TB/s1 TB/s通信带宽900 GB/s900 GB/s64 GB/s通信时延~1 us~1 us~10 us售价40000$15000$1600 NVIDIA 的算力表里面油水很多,比如 H100 TF16 算力写的是 1979 Tflops,但那是加了 sparsity(稀疏)的,稠密的算力只有一半;4090 官方宣传 Tensor Core 算力高达 1321 Tflops,但那是 int8 的,FP16 直只有 330 Tflops。这篇文章的第一版就是用了错的数据,H100 和 4090 的数据都用错了,得到的结论非常离谱。
H100 这个售价其实是有 10 倍以上油水的。
2016 年我在 MSRA 的时候,见证了微软给每块服务器部署了 FPGA,把 FPGA 打到了沙子的价格,甚至成为了供应商 Altera 被 Intel 收购的重要推手。2017 年我还自己挖过矿,知道什么显卡最划算。后来在华为,我也是鲲鹏、昇腾生态软件研发的核心参与者。因此,一个芯片成本多少,我心里大概是有数的。
🌈个人主页:聆风吟
🔥系列专栏:算法模板、数据结构
🔖少年有梦不应止于心动,更要付诸行动。
文章目录 📋前言一. ⛳️模拟栈1.1 🔔用数组模拟实现栈1.1.1 👻栈的定义1.1.2 👻向栈顶插入一个数 x(进栈操作)1.1.3 👻从栈顶弹出一个元素(出栈操作)1.1.4 👻判断栈是否为空1.1.5 👻查询栈顶元素 1.2 🌟模板提取(重点)🌟 二. ⛳️题目练习2.1 题目2.2 输入样例2.3 输出样例2.4 c++代码 📝结语 📋前言 💬 hello! 各位铁子们大家好哇,我们上期已经学习了双链表的算法模板,不知道大家都已经掌握了吗?如果你还有缺漏可以通过下面专栏自行跳转学习,今天作者又又又给大家带来了栈的算法模板详细讲解,让我们一起加油进步。
📚 系列专栏:本期文章收录在《算法模板》,大家有兴趣可以浏览和关注,后面将会有更多精彩内容!
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
一. ⛳️模拟栈 1.1 🔔用数组模拟实现栈 1.1.1 👻栈的定义 栈(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。如下图是栈的示意图:
1.1.2 👻向栈顶插入一个数 x(进栈操作) 根据栈的定义可知,我们可以将数组看作是横放的栈的示意图,即将数组的首元素位置看作栈底、当前元素的位置看作栈顶,便可以实现数组模拟栈的相关操作。如果我们要向栈顶插入一个元素,将栈顶指针向后移动一位将元素插入进去即可。如下图所示:
代码展示(建议结合图示看注释):
//top表示栈顶 int stk[N], top = -1; // 向栈顶插入一个数x stk[++top] = x; 1.1.3 👻从栈顶弹出一个元素(出栈操作) 根据上面所知,如果我们要从栈顶弹出一个元素,我们只需要将栈顶指针向前移动一位即可。如下图所示:
代码展示(建议结合图示看注释):
// 从栈顶弹出一个数 top--; 1.1.4 👻判断栈是否为空 根据上面所知,如果我们要判断栈是否为空,我们只需要判断栈顶指针是否指向数组首元素左边的位置(即判断top是否等于-1位置)。如下图所示:
代码展示(建议结合图示看注释):
// 判断栈是否为空,如果 top >= 0,则表示不为空 if (top >= 0) { //输出栈不为空 } else { //输出栈为空 } 1.
一 插件简介
1.1 当HBuilderX中提供的能力无法满足App功能需求,需要通过使用Andorid/iOS原生开发实现时,可使用App离线SDK开发原生插件来扩展原生能力。
1.2 插件类型有两种,Module模式和Component模式
Module模式:能力扩展,无嵌入窗体的UI控件。大部分插件都是属于此类,比如调用计步器API。代码写法为通过js进行require,然后调用该插件对象的方法。如涉及一些弹出框、全屏ui,也仍然属于Module模式。类似于前端里的js sdk。Component模式:在窗体中内嵌显示某个原生ui组件。比如窗体局部内嵌某个地图厂商的map组件,上下混排其他前端内容,就需要把这个原生地图sdk封装为Component模式。代码写法与vue组件相同,在template里写组件标签。类似于前端里的vue组件。 1.3 插件的使用:原生插件开发后,可以上插件市场,也可以不上。如内部使用,则无需上架插件市场。 如需上插件市场,则必须按指定格式压缩为zip包 二 插件的开发
2.1 插件必须在uni-sdk的基础上进行开发,可以快速的下载离线uni-sdk,导入UniPlugin-Hello-AS示例工程,也可以自己新建一个原生android项目,拷贝不要的包和资源进行开发。
2.2 离线uni-sdk下载地址: https://nativesupport.dcloud.net.cn/AppDocs/download/android.html,里面包含必要资源和示例工程。
2.3 上一篇已经新建了一个android项目,并离线打包成功了uni资源项目,下面就在这基础上开发插件
2.4 androidStudio项目右击新建一个插件Module
2.5 选择library,填写module名字,点击finish
2.6 然后在app的build.gradle里面依赖该module
implementation project(':mylibrary') 2.6 module里面build.gradle配置相关依赖库
uniapp-v8-release.aar是扩展module主要依赖库,必须导入此依赖库
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs')
com.alibaba:fastjson 也是必须的也依赖上,后面json通信会用到
implementation 'com.alibaba:fastjson:1.2.83'
同时为了避免和主app冲突,将全部依赖类型换为compileOnly,意思只本module有效
dependencies {
compileOnly 'androidx.appcompat:appcompat:1.5.0'
compileOnly 'com.google.android.material:material:1.6.1'
compileOnly 'com.alibaba:fastjson:1.2.83'
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs')
}
2.7 创建TestModule类,必须继承 UniModule 类
扩展方法必须加上@UniJSMethod (uiThread = false or true) 注解。UniApp 会根据注解来判断当前方法是否要运行在 UI 线程,和当前方法是否是扩展方法。UniApp是根据反射来进行调用 Module 扩展方法,所以Module中的扩展方法必须是 public 类型。 示例:创建两个方法
centos7.9安装k8s 1. 安装k8s1.1 环境准备1.2 配置源1.3 安装1.4 docker换源1.5 开机启动1.6 拉取镜像1.7 初始化问题1问题2 1.8 查看状态1.9 安装网络插件问题1问题2 2. 安装图形界面3. 重要:证书过期问题4. 引用 1. 安装k8s 卸载旧的k8s(一开始随便找了个教程, 跟着做,没安装成功)
sudo yum remove -y kubeadm kubectl kubelet kubernetes-cni kube* sudo yum autoremove -y rm -rf /etc/systemd/system/kubelet.service rm -rf /etc/systemd/system/kube* sudo rm -rf ~/.kube sudo rm -rf /etc/kubernetes/ sudo rm -rf /var/lib/kube* 1.1 环境准备 # 关闭 selinux setenforce 0 #实时动态关闭 selinux sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config #禁止重启后自动开启 # 关闭交换分区 swapoff -a #实时动态关闭交换分区 sed -i '/ swap / s/^/#/' /etc/fstab #禁止重启后自动开启 # 网络配置文件 cat <<EOF > /etc/sysctl.
目录
题目:
答:
题目: 进程p1,p2,p3,p4,p5和p6的前趋图如下图所示。用PV操作控制这6个进程之间同步与互斥
的程序如下,程序中的空(1)和空(2)处应分别为________,空(3)和空(4)处分别为________,
空(5)和空(6)处应分别为________.
begin
S1 , S2, S3, S4, S5, S6: semaphore; //定义信号量
S1:=0; S2:=0; S3:=0; S4:=0; S5:=0; S6:=0;
Cobegin
process P1 process P2 process P3 process P4 process P5 process P6
begin begin begin begin begin begin
P1执行; (1); P(S2) (4) P(S4); (6)
V(S1); P2执行 P3执行 P4执行 P5执行 P(S6)
end; (2); (3); V(S5) (5) P6执行
Coend; end; end; end; end; end;
end;
答: PV操作中的P代表通过,V代表释放
TortoiseGit 安装、配置及使用详细教程 大家好,给大家推荐一款简易的桌面端 Git 工具——> TortoiseGit,使用其完成 Git 的克隆、提交、推送、日志查看、版本差异比较等功能,摆脱命令行。
首先,确保本地安装了 Git,未安装的可以通过以下链接参考完成 Git 的安装。
博主:Java知识技术分享
链接:Git 的安装与配置教程-超详细版
另外,这里我要补充下以上链接中的缺少的 Git 简介。
Git是一个分布式版本控制系统,它可以帮助开发人员跟踪文件的变化,协作开发代码,并且管理项目的版本。通过Git,开发人员可以在不同的分支上进行并行开发,合并代码,回滚到历史版本,以及管理代码的变更历史。Git也提供了远程仓库的功能,使得团队成员可以共享和协作开发代码。Git已经成为许多软件开发团队中常用的版本控制工具。
这篇文章的制作本意是公司其他部门人员需要一个文件版本管理系统,但找了很多系统发现要么是国外的系统使用不方便,要么是一些知识库管理系统版本控制做的不是很好,由于我本身是一个开发者,所以感觉 Git 使用就很方便,但为了给其他部门同事出一个教程,才有了今天的文章。所以文章最后的示例将会使用Word、Excel、PPT 三大办公常用文件。大家也可以参考此来完成自己的文档管理。
OK,正文开始···
一、下载 TortoiseGit TortoiseGit 官网下载地址:TortoiseGit Download
为了方便使用,需要下载两个文件。分别是 TortoiseGit 的安装包和中文语言包。
注意:此处请根据自己的系统版本选择响应的 TortoiseGit 版本进行下载。
下载完成后的安装包如下所示:
二、安装 TortoiseGit 1、运行 TortoiseGit 安装包后直接点击 ’Next‘ 2、此处显示的是一些相关协议,点击 ’Next‘ 即可 3、此处可以选择安装的位置,也可以直接默认,占用空间并不大,完成后点击 ’Next‘ 4、点击 ’Install‘,进行安装 5、安装完成直接点击 ’Finish‘ 完成安装,弹出配置页 注意:此处暂不进行配置,也不要关闭此配置页,先完成 TortoiseGit Language 安装后再来配置。
三、TortoiseGit Language 安装 1、运行 TortoiseGit Language 安装包后直接点击 ‘下一步’ 2、等待安装完成后,点击 ‘完成’ 即可 四、配置 TortoiseGit 还记得刚才安装 TortoiseGit 完成后打开的配置页吗,从此处继续咱们的下一步
背景 想直接通过域名访问k8s上的服务. 想到k8s上可以直接通过ingress配置. 不过ingress默认启动的端口3xxxxx. 一般不可能让用户访问我们的服务加上端口. 所以现在要解决直接通过80端口访问ingress的问题.
方案 修改ingress-nginx端口(这个是在网上搜到的方案, 但未选择) 这个要修改k8s配置, 默认k8s上服务是不能开80端口的
直接在服务器通过docker部署个nginx 80端口, 然后配置这个nginx将所有流量转发到ingress-nginx. 执行过程中遇到的问题:
通过ingress配置转发到后端 – 正常通过nginx转发到ingress – 正常通过nginx转发到ingress, 再通过ingress转发到后端 --异常 举个例子: 我ingress 端口是30181, 服务端口是30444(服务有接口/admin), 额外部署的nginx端口80. 域名abc.cn 解析到nginx所在到IP
访问 -> abc.cn:30181 – 正常,直接到了ingress
访问 -> abc.cn:30181/admin – 返回200(正常,通过ingress转发到了服务)
访问 -> abc.cn – 正常,通过nginx转发到了ingress
访问 -> abc.cn/admin – 返回404. 异常, 预期返回200.
通过查看nginx的access.log. 这个请求通过额外部署的nginx转发到了30181端口. 但是查看ingress-nginx的日志, 请求没有进来.
额外部署的nignx 配置如下
server { listen 80; server_name abc.cn; #server_name _; location / { proxy_pass http://172.
目录
创建Message组件
注册为全局组件
添加原型方法
使用方法
总结
在Vue项目中,有时候我们需要在任何组件内呈现一个消息通知,比如保存成功、提交错误等等。要实现这个功能,我们可以创建一个自定义的全局组件并注册为原型方法。
创建Message组件 首先创建一个Message.vue组件:
<template> <transition name="fade"> <div v-if="isShow" class="message"> <slot></slot> </div> </transition> </template> <script> export default { data() { return { isShow: false } } } </script> <style> .message { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 10px 20px; border-radius: 4px; background: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, .15); } .fade-enter-active, .fade-leave-active { transition: opacity 0.5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style> 这个组件通过v-if和过渡效果控制消息显示和隐藏。
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox from PyQt5.QtCore import Qt from untitled import * class MainWindow(QMainWindow, Ui_Form): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.pushButton.setStyleSheet(''' QPushButton {text-align : center; color:red; font: bold; border-color: red; border-width: 5px; border-radius: 10px; padding: 6px; height : 14px; border-style: outset; font : 14px;} QPushButton:pressed {text-align : center; background-color : red; color:red; font: bold; border-color: red; border-width: 5px; border-radius: 10px; padding: 6px; height : 14px; border-style: outset; font : 14px;} ''') self.
文章目录 复盘与一周总结2967. 使数组成为等数数组的最小代价(中位数贪心 回文数判断)2968. 执行操作使频率分数最大(中位数贪心 前缀和 滑窗) 复盘与一周总结 wa穿了第3题,赛时其实想到了思路:中位数贪心,从中位数开始,用左右指针找到第一个回文数,与该回文数的代价就是答案。但是没有考虑到左右指针同时找到回文数的情况,wa了一发之后开始改。用一个vector保存代价,只要数组长度大于2就返回其中的较小值。但是没有注意到自己的算法是左右指针同时找,可能出现同一个指针找到两次回文数的情况,此时就不是左右指针分别找到一次回文数。后面改成:数组长度大于5就返回最小值才ac,赛后重写用第一次的思路写了一遍,很快就ac了
现在想想,自己能很快想到正解,但是算法实现的却不是正解,而且赛时还没发现,甚至以为是思路错了,还往平均数那块想了会。只能说,自己在算法实现这块考虑得不仔细吧,下次别着急,想慢点
至于说第4题,赛时完全没有想到中位数贪心,想到哪了呢?我在考虑数组中数的出现次数,出现次数更多的数,最后的代价是否会小于出现次数更小的数。甚至想到:若一个数出现次数超过一半,那么把所有数变为这个数,此时的频率是否最大?接着就对着这个贪心结论证啊证,最后不了了之。但题目的关键点是:操作次数有限,那么就要考虑如何操作能尽可能少的使用操作次数,这样就能想到中位数了
周末这几场打下来,发现自己最大的问题就是:题意的理解。一是读假题,连题目在说什么都没搞清楚,甚至是自以为搞清楚,然后自欺欺人地想算法去了,如abc的E题,小白赛的E题。二是没有抓住题意的重点,只要是稍有难度的题,都需要抓住关键点不断地分析,如这次的第4题,小白赛的F题
问题反而是出现在阅读理解上
2967. 使数组成为等数数组的最小代价(中位数贪心 回文数判断) 2967. 使数组成为等数数组的最小代价 - 力扣(LeetCode)
根据中位数贪心,将数组排序后,从中位数开始,分别向左和向右找到第一个回文数并计算代价,数组两个代价中较小的即可
class Solution { public: bool f(string s) { int l = 0, r = s.size() - 1; while (l < r) { if (s[l] != s[r]) return false; l ++ , r -- ; } return true; } long long minimumCost(vector<int>& nums) { sort(nums.begin(), nums.end()); int mid; if (nums.
SemEval2016 — HanLP Documentation
角色与动词关系例子理解、补充、区分施事关系Agt我跳到茅坑 跳到->我动作的发出者,心里活动触发的外在动作行为,主观感事关系Aft我听到炮声 听->我心里感受,五官感受,心理活动(思念),主观当事关系Exp我掉到茅坑 掉到->我客观规律,自然现象,存现,意外领事关系Poss我有2个男朋友 有->我领属,拥有,包含关系受事关系Pat我送他一朵菊花 送->菊花动作的承受者,送的动作指向的角色客事关系Cont我听到炮声 听到->炮声动作指向的客观事,物成事关系Prod我写了一本书 写->书动作指向了该动作的结果源事关系Orig我缴获了大炮 缴获->大炮来源,(发现这里有出入,后面修复)涉事关系Datv我送他一朵菊花 送->他送花这个事件指向的角色 (送->花)->他
前言 今天应师父要求,写一篇利用docker搭建渗透靶机的文章来记录一下自己的菜鸟时光,要求是小白照着一步一步走就能搭建成功(即使我也是小白)。想来想去还是从docker的安装说起吧。
一、docker的安装及相关配置 系统版本:centos7
内核版本:3.10.0
1、yum更新 yum update 2、安装需要的软件包 yum install -y yum-utils device-mapper-persistent-data lvm2 3、设置yum源 国外镜像一般很难访问,建议配置阿里云镜像。
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 4、安装docker 这里默认安装最新版本。
yum install docker-ce 5、启动并加入开机启动 systemctl start docker systemctl enable docker 6、验证安装是否成功 (有client和service两部分表示docker安装启动都成功了) docker version 7、配置加速器 这个纯属个人喜好,因为dockerhub国内访问的限制加上国内几个加速器个人感觉不好用,所以我选择了阿里云的容器镜像服务中的加速器(免费的,个人感觉超赞)。
获取镜像加速器后,阿里云非常贴心的给出了使用配置步骤:
二、对于docker的理解和基本使用命令 1、仓库、镜像和容器之间的关系 镜像:本人理解的docke镜像就像Vmware虚拟及里面的快照,可恢复成运行中的虚拟机,也可以保存在本地。容器:镜像运行起来便是容器。如果按照上面这个比喻的话,容器就是快照恢复后的虚拟机。仓库:用来存储镜像,比较大的有docker hub和阿里云。 2、仓库、镜像和容器之间的命令使用 仓库—镜像:从仓库拉取(下载)镜像docker pull [仓库地址及镜像版本]镜像—仓库:从本地上传镜像到仓库docker push [仓库地址及镜像版本]镜像—容器:镜像运行成容器docker run [参数] [镜像名称或ID]容器—镜像:将容器保存为镜像docker commit [容器ID] [参数及其他] 三、docker搭建php7cms靶场 1、php7cms下载 本次搭建靶机是为了写poc验证漏洞,对应的php7cms版本是2018-10-09,
下载地址:https://www.a5xiazai.com/php/138976.html
下载后:
2、拉取所需环境的docker镜像 这个php7cms需要php版本为php7以上和mysql数据库5.6以上,而docker的强大之处之一就是非常方便,可以拉取别人搭建好的环境直接使用。
对应这次搭建靶场所需环境,本人推荐一个链接,里面有php几个常用版本的lamp镜像。
比如我们要拉取php7.1版本:
docker pull thiagobarradas/lamp:php-7.1 然后就进入了等待中:
当然也可以搜索官方的镜像仓库:docker search [关键词]
:::info
什么是垃圾回收
:::
垃圾回收是一种自动管理内存的机制,用于检测和释放程序中不再被引用的内存对象,以避免内存泄漏和提高程序性能。
对象的生命周期:在程序中,对象在被创建时分配内存空间,然后在不再被引用时,这部分内存应该被释放。对象的生命周期包括创建、使用和不再被引用的阶段。引用:引用是指对象的访问或指向对象的指针。当没有引用指向一个对象时,该对象就成为垃圾。垃圾回收器:垃圾回收器时负责检测和回收不再被引用的内存对象的组件。它周期性地运行,找到并释放那些不再被引用的对象所占用的内存空间。垃圾回收算法:垃圾回收器可以使用不同的垃圾回收算法来确定哪些对象是可达的(仍然被引用),哪些是不可达的(成为垃圾)。常见的回收算法包括标记-清楚算法、复制算法、标记-清理算法等。 垃圾回收的目标是优化内存使用,减少内存泄露的可能性,提高程序的性能和稳定性。开发人员通常无需手动释放对象,而是依赖于垃圾回收器来管理内存。这使得开发更加方便,同时避免了一些与手动内存管理相关的常见错误。
一.如何判断对象可以回收 1.1 引用计数法 引用计数法是一种垃圾回收算法,主要思想就是为每个对象维护一个引用计数,每当有一个引用指向对象时,计数加一,当引用失效时,计数减一,当计数为零时,表示对象不再被引用,可以被回收。
但是,引用计数法有一些问题,其中最主要的问题就是无法解决循环引用的情况。比如A对象引用B对象,B对象又引用A对象,形成了一个环,这就是循环引用。即使这组对象不再被其他对象引用,它们之间的引用计数也永远不会变为零,导致这组对象无法被垃圾回收。
由于Java中存在复杂的数据结构和对象引用关系,引用计数法无法很好地处理这些情况。因此,Java使用可达性分析算法。
1.2 可达性分析算法 这个算法的核心思想是通过根集合(包括类变量、本地变量等)作为起点,通过一系列的引用关系追踪对象的可达性,确定哪些对象是可达的,哪些是不可达的,然后回收不可达对象。
基本原理和步骤:
根集合:可达性分析的起点是一组称为根集合的引用对象,包括类变量(static变量)、本地变量以及一些特殊的引用。这些对象是程序执行的起点,它们始终被认为是可达的。跟踪引用关系:从根集合开始,通过引用关系逐步跟踪对象的引用,形成一个引用链。如果一个对象可以通过一系列的引用关系连接到跟集合中的任何一个对象,那么这个对象就是可达的。标记阶段:在追踪引用关系的过程中,标记所有被访问到的对象为"活跃"或"可达"。这个阶段确保只有可达的对象被保留,而不可达的对象被标记为"非活跃"或"不可达"。清除阶段:在标记阶段结束后,清除所有未被标记为"活跃"的对 象,即不可达对象。这些不可达对象的内存空间将被释放,成为可用于存储新对象的空间。 这就好像是BFS算法,只要是从根节点搜索到的都保留,没搜索到的都舍去。
为啥解决了循环引用问题呢?
一组对象之间是循环引用,那么只要外界没有对象引用它们其中任意一个,那么这组对象就永远不会被跟踪到,那么就会被认为是不可达的,在清除阶段就会被回收。
1.3 四种引用 :::info
强引用
:::
强引用时最常见、最普遍的引用类型,当一个对象具有强引用时,垃圾回收器不会回收这个对象,即使系统中内存不足。只有当不再有任何强引用指向对象时,才有可能被垃圾回收器回收。
举个例子:
public class StrongReferenceExample { public static void main(String[] args) { // 创建一个对象并赋予强引用 Object obj = new Object(); // obj 强引用仍然存在,对象不会被垃圾回收 System.out.println("Object is still reachable."); // 将 obj 引用置为 null,此时没有强引用指向对象 obj = null; // 此时对象变为不可达,但不一定会立即被回收 // 垃圾回收器会在适当的时候回收不可达的对象 // 一旦垃圾回收器决定回收对象,它的 finalize() 方法(如果有)将被调用 // 最终,对象的内存将被释放 System.
最近在写项目的时候遇到一个问题,需求是表格中的备注字段只显示2行,超出显示…,鼠标悬浮显示完整。
代码如下:
//这里我直接封装成了一个全局组件,方便在项目里使用。 <template> <div v-if="value" v-html="value.replace(/\n/g, '</br>')" :title="value" class="table_textarea_cell" style="-webkit-box-orient: vertical" ></div> </template> <script> export default { name: "TableTextareaCell", props: { value: { type: String, default: "", }, }, data() { return {}; }, }; </script> <style lang='scss' scoped> .table_textarea_cell { overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; } </style> overflow: hidden; // 超出的文本隐藏
text-overflow: ellipsis; // 溢出用省略号显示
white-space: nowrap; // 溢出不换行
display: -webkit-box; // 将对象作为弹性伸缩盒子模型显示。
-webkit-line-clamp: 2; // 这个属性不是css的规范属性,需要组合上面两个属性,表示显示的行数。
文章目录 写在最前面的复盘D - Erase LeavesE - Takahashi Quest 写在最前面的复盘 前三题属于是凑数题,下次争取快点a掉,这次wa了一次
C题写了个三指针,从小到大枚举出满足题意的数,其实可以直接暴力枚举满足题意的数,但是会有重复的,用set去重即可,赛时没想到,三指针磨了很久。原来暴力也是门艺术,什么时候适合暴力也是门学问啊,自己对于这块的理解确实不够深
以为D题读懂了题意,然后写写写,debug了很久,赛后重写了一遍,也就只有个dfs,5分钟写完+debug就过了。现在想想应该是刚打完acc的影响,脑子转不动了。还有就是题意没有想清楚就开写,导致最后一直debug
E题也想到了贪心,但是题意理解做了(又读了假题),想了一个巨复杂的贪心,看着ac人数有点多,发现不对劲。但是已经被假贪心摧残,脑子转不动,有心无力直接下班
最后只能说,以后先读懂题,再开始头脑风暴,读假题真的难绷
D - Erase Leaves D - Erase Leaves (atcoder.jp)
统计以x为根节点的子树中,节点的数量(包括x自己),用cnt数组存储
对于1号节点的所有子节点i, i + 1, ..., j - 1, j,计算cnt[i] + cnt[i + 1] + ... + cnt[j - 1] + cnt[j],最后删去最大的cnt即为答案
cnt数组用dfs就能求出
#include <bits/stdc++.h> using namespace std; const int N = 3e5 + 10; vector<int> g[N]; int cnt[N]; bool st[N]; int dfs(int x) { st[x] = true; for (auto y : g[x]) { if (!
今天在项目中刚好遇到一个到从当前小程序中跳转到另一个小程序,下面分享一下我总结的几个比较简单的跳转方式吧。
方式一: 1.配置要跳转的appid和小程序页面路径 wx.navigateToMiniProgram({ appId: '目标小程序appid', path: '目标小程序页面路径', //develop开发版;trial体验版;release正式版 envVersion: 'release', success(res) { // 打开成功 console.log("跳转小程序成功!",res); } }) 如果不给path属性是默认跳到目标小程序首页,如果想跳到其他页面就要配置path属性,这样就能实现从当前微信小程序跳转到另外一个小程序啦~
方式二: 如果不知道目标小程序的appid和页面路径怎么办? 也有办法,就是用shortLink属性实现链接跳转,点击小程序右上角,选择“复制链接”就可以啦
代码实现
wx.navigateToMiniProgram({ shortLink:'目标小程序链接', //develop开发版;trial体验版;release正式版 envVersion: 'release', success(res) { // 打开成功 console.log("跳转小程序成功!",res); } }) 第二种方式就是不知道小程序的appid的时候可以使用,不过用shortLink属性跳转的话,默认是跳到目标小程序的首页喔~
关于想用appid实现跳转,但是又没有源码的情况下,怎么拿到小程序页面路径呢? 其实在微信公众平台是可以通过设置获取到页面路径滴~具体要怎么做呢,我们看下个章节吧 《获取小程序页面路径》
安装包需从夸克网盘自取:
链接:https://pan.quark.cn/s/373ffc9213a1
提取码:N7PW
1.将安装包解压
2.以管理员的身份运行文件夹中的setup文件
3.点击高级选项--->我有文件安装密钥
4. 选择【是】,进入下一步
5.输入密钥
05322-36228-06991-12654-51812-34369-14072-44298-22786-36732-05503-35033-50900-29808-05166-12170-05630-02560-02687-62114-45079-42917-06281-13007-19512-18270 6.点击浏览,选择安装包解压后的文件夹里【Crack】 文件夹中的【license.lic】文件
7.选择安装的文件夹
8.选择需要安装的产品(自己用不到的产品建议不要勾选,徒增磁盘容量)
9. 勾选【将快捷方式添加到桌面】
10.安装完毕后,进入【Crack】文件家,选择【libmwlmgrimpl.dll】进行复制
11.右键点击桌面的MATLAB,选择【打开文件所在位置】--->打开【win64】文件夹
继续打开【win64】文件夹中的【matlab_startup_plugins】文件夹--->打开【lmgrimpl】文件夹
12.点击空白处选择粘贴,选择【替换目标中的文件】
13. 双击启动桌面的MATLAB,至此安装完成!
锁可以升级,但不能降级。即:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁是单向的
32位的jvm里面对象头示例
无锁 :biased_lock(0),01。这个时候的biased_lock 是0的时候就表示了现在无偏向
偏向锁:biased_lock设成1代表该对象锁成为了偏向锁,并且在原来存hashcode的地方将偏向的线程记录了,这个时候调用对象的hashcode()方法会使偏向锁生效
轻量级锁 :当一个线程尝试获取一个对象的锁时,如果发现该对象处于偏向锁状态,但是不是自己持有偏向锁,那么就需要升级为轻量级锁。轻量级锁的实现通常也会使用CAS操作来尝试获取锁,这个时候会通过替换锁对象头的引用来确定,如果当前锁对象头不是锁对象本身的地址那么就证明在同一时间发生了竞争,竞争情况下可能膨胀为重量级锁。00(指轻量级锁状态)
重量级锁:升级后前面持有锁的线程在还回去的时候发现了还不回去锁对象已经创建了Monitor,这个时候线程就需要将owner置为null,然后唤醒阻塞中的线程。10(指重量级锁状态)
重量级锁也有优化,在重量级锁的情况下线程在没有获取到锁的情况下不会立即阻塞,阻塞是需要切换上下文的是需要耗费资源的,线程会进行一个自旋操作继续获取锁万一锁释放了呢(自旋耗费CPU性能,单核CPU自旋就是浪费),jvm会根据前面自旋成功的情况给我们智能的调节自旋的次数。
一、简介 用C#编写了几个单片机上位机模板。可定制!!!
二、效果图