GraftCopolymer's Blog~~Welcome🤗

A little tech site, welcome

注意,本文所说的加载,是类加载这个过程的第一个阶段,而不是指的类加载的全部过程

加载(Loading)

从不同的来源将类文件(.class)转化为二进制流加载到内存中,来源可以是本地磁盘、jar包中的类、甚至可以从网络上动态下载类。然后会通过这个二进制流将数据转化成一个代表类的Class对象,并且在元空间中存储该类的信息

这里有一个容易混淆的概念,方法区,永久代以及元空间的关系。方法区只是一个规范,而永久代和元空间是对这套规范的实现,永久代是JDK8之前的实现方式,而元空间是JDK8及之后的实现方式。永久代和元空间的区别在于,永久代在JVM的堆内存中,而元空间在本地内存中,即操作系统中可用内存大小,其大小不受-Xms-Xmx的限制,减少了OOM发生的频率。

静态常量池

编译期确定,存储在.class文件中,可以通过javap -v -c XXX.class查看

动态常量池

又称运行时常量池,是JVM在加载类时,将静态常量池加载到元空间后的产物,这意味着它是位于元空间的。动态常量池中的数据与静态常量池中的数据基本相同,但是有着更加灵活的特性,包括可实时动态向其中添加新的对象。

动态常量池还与动态解析相关,其中存放的符号引用会在解析成功后替换为直接引用,若解析失败,则会被JVM标记,防止下一次再次尝试解析

阅读全文 »

RecyclerView之所以能支撑起大量列表项显示的场景,离不开其优秀的缓存机制

多级缓存

RecyclerView采用了多级缓存机制,按照优先级排列如下:

缓存 保存位置
一级缓存 mChangedScrap、mAttachedScrap、mCachedViews
二级缓存 mViewCacheExtension
三级缓存 mRecyclerPool

一级缓存

mChangedScrapmAttachedScrapmCachedViews所缓存的ViewHolder的类型是不同的(此处不是指的viewType类型,而是指的不同状态下的ViewHolder)

  • mChangedScrap:数据有变化的ViewHolder将会缓存在此处,例如通过notifyItemChanged(position)方法通知RecyclerView某个索引位置对应的组件数据有变化,通过该字段缓存的View无需重建,但是需要重新绑定数据(调用onBindViewHolder方法)
  • mAttachedScrap:目前在屏幕上仍然可见的组件所对应的ViewHolder将会缓存在此处,在页面绘制时,无需重建View且无需重新绑定数据
  • mCachedViews:最近被移出(例如用户滑动)或即将被移出的View所对应的ViewHolder将会缓存在此处,重绘时无需重建ViewHolder且也无需重新绑定数据。主要场景是用户在短距离内快速滑出又快速划入,所以这个缓存列表并不是很大,默认长度为2,类型为SparseArray(Android系统针对整数类型的键的Map优化后的数据结构,适用于键为整数的场景)
阅读全文 »

在Kotlin中,弱化了静态方法这个概念,使得我们想要创建静态方法变得不那么直观,并且,有些方式创建出来的"静态方法"还不是真正的静态方法,这里一一介绍

假的静态方法

使用这类方式创建出来的"静态方法"只是在调用上看起来像是静态方法,但在jvm看来并不是

单例类 object

使用object关键字标注的类会被视为单例类,Kotlin会保证这个类在运行时不会有两个及以上的实例存在,例如:

阅读全文 »

索引的结构

大部分DBMS都会采用B+树实现索引,这种数据结构可以提高查询时的效率,假设有如下表:

PID BID AMOUNT
1001 B1 5000
1005 B2 3000
1010 B3 9000

我们可以对PID建立索引(若PID为主键,数据库会为我们自动创建索引),索引呈现出来的树结构如下:

1
2
3
       [1005]
/ \
[1001] [1010]

对于任意一个非叶子结点,左子树结点的值都比其小,右子树结点的值都比其大(若为字符串则根据字符串顺序,其他数据类型类似),根据这个特性,比如我们要查找1010,首先进入1005结点,发现1010比1005大,则向右子树查找,直接就找到了1010,仅需比较2次。而如果不使用索引,则会顺序查找,需要比较3次,对于大数据量时,使用索引的效率提升会更加明显。

阅读全文 »

Dart多线程 Isolate 入门

什么是Isolate

Isolate就是Dart中的多线程,之所以不称为Thread,而是称为Isolate,是因为它与传统线程的概念有所区别,见如下表格

特性 Isolate Thread
内存共享 不共享内存 可以共享内存
通信方式 ReceivePort和SendPort 共享变量或synchronized关键字
数据竞争 无数据竞争 可能有数据竞争
开销 开销较大 相对轻量
任务调度 Dart运行时管理,适合CPU密集型任务 由操作系统管理

Isolate的创建之所以比Thread开销要大,是因为Isolate在创建时需要创建更多的资源,例如事件循环队列、微任务队列,独立的内存堆以及独立的垃圾回收器,并且每个 Isolate 需要单独初始化 Dart 运行时环境,包括 Dart 代码执行上下文,而线程只需要共享进程的执行上下文。

使用场景:需要耗时计算时

阅读全文 »

除了向服务器发起请求验证token是否过期以外,一些类型的token是直接将过期时间储存在token之中的,我们可以通过这一点在客户端本地验证token是否过期。

token的构成

token其实就是三个东西以小数点分隔开的字符串

阅读全文 »

对于需要频繁登录操作的服务器,每次都要输入服务器用户的密码显得过于麻烦,幸好SSH允许我们通过密钥来实现免密登录,本以为配置起来挺简单的,但还是遇到一些坑,所以记录一下

在服务器生成密钥

首先要在你想免密登录的服务器的用户的主目录创建目录.ssh来存放密钥文件,cd.ssh目录,然后使用以下命令生成密钥:

1
ssh-keygen -t rsa

也可以不用rsa算法生成,但一定要保证待会在自己电脑上生成密钥的算法和在服务端的算法一致,这点很重要,因为有可能服务器的ssh版本与你电脑上的版本不一致,如果没有指定-t参数,就会使用默认的算法生成,而两者的默认算法并不一定一样。所以无论你想用什么算法,都明确指定-t参数。

阅读全文 »

从Java14开始,引入了新的关键字record,该关键字的能让我们快速地编写一些数据类(Data Class):

基本语法

1
public record User(String username, String password){}
阅读全文 »

前文回顾

上一篇文章中,我们讲完了一些基本概念,包括,注册,事件系统和游戏的两个端,这一篇教程我们会添加第一个自定义物品——闪电剑,要实现的效果是,左键攻击到生物实体时,会在生物的位置降下闪电,右键一个方块时,会在方块的位置降下闪电。本篇教程与前一篇跨度比较大,没有Java基础的朋友可以直接走了。

Where to start?

本教程可能在有些读者看来略显啰嗦,但是我仍然不准备像其他教程那样,直接甩一堆代码上来让大家理解,我会带大家从原版代码出发,一步步分析,模仿原版的实现,这样才能让大家对Minecraft的底层知识有更深刻的理解,并且写出更符合Minecraft本身的风格的模组。

阅读全文 »

.gitignore语法规则

  • 使用 # 来编写注释,注释的内容不会被git视为忽略规则
  • 忽略规则以/结尾表示忽略的对象是一个目录,而不是文件
  • 忽略规则以/开头则表示忽略的对象的路径是相对于项目的根目录,如果某个规则的开头不是/,那么这条规则的匹配路径会相对于当前.gitignore文件的位置
  • 在忽略规则最开头加上!表示不忽略该规则指定的对象(相当于取反)
  • 可以使用标准glob模式匹配(glob模式就是Linux的shell下简化的正则表达式)
阅读全文 »
0%