Closed peteryuanpan closed 3 years ago
"子牙" 是1个String,1个char new String("真帅");是2个String,2个char 那么合并起来是4个String,3个char(Ubuntu internal jdk8环境下) 至于为什么windows下JDK8跑出来是3个String,3个char,老师不知道,直接跳过了...
数组长度对应起来是一个字节码指令:arraylength 因此来看字节码解释器:https://github.com/peteryuanpan/openjdk-8u40-source-code-mirror/blob/master/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp#L1794
CASE(_arraylength):
{
// 从栈中获取到arrayOop
arrayOop ary = (arrayOop) STACK_OBJECT(-1);
// 检查oop不为Null
CHECK_NULL(ary);
// 获取arrayOop的length,并往栈中设置值
SET_STACK_INT(ary->length(), -1);
// 更新程序计数器
UPDATE_PC_AND_CONTINUE(1);
}
// Accessors for instance variable which is not a C++ declared nonstatic
// field.
int length() const {
// 取对象的首地址 + 数组长度在内存中的偏移,转为int指针,取值
return *(int*)(((intptr_t)this) + length_offset_in_bytes());
}
这个内存中的偏移是如何计算的 来到上面一点
// The _length field is not declared in C++. It is allocated after the
// declared nonstatic fields in arrayOopDesc if not compressed, otherwise
// it occupies the second half of the _klass field in oopDesc.
// 翻译:在C++中没有声明长度字段,如果不压缩,则在arrayOopDesc中声明的非静态字段
// 之后分配,如果压缩的话,它将占用oopDesc中_klass字段的后半部分
static int length_offset_in_bytes() {
// UseCompressedOops压缩的是对象指针的长度(指针压缩)
// UseCompressedClassPointers压缩的是Klass对象指针的长度
return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
sizeof(arrayOopDesc);
}
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass; // 8B
narrowKlass _compressed_klass; // 4B
} _metadata;
1.union _metadata 是联合体,含义是 _klass 和 _compressed_klass 只能用一个,整个联合体占8B,它的含义是类型指针,在不压缩情况下占8B,压缩情况下占4B 2.length_offset_in_bytes函数的注释:在C++中没有声明长度字段,如果不压缩,则在arrayOopDesc中声明的非静态字段之后分配,如果压缩的话,它将占用oopDesc中_klass字段的后半部分。这句话的含义是,如果压缩了话,联合体8B中,前4B给类型指针用,后4B给数组长度用
通过这段代码分析下,其中数组长度为3 在开启指针压缩情况下,用HSDB分析,发现0x00000003f800016e,其中0x00000003就是数组长度的值,它占用了类型指针的一半内存 在关闭指正压缩情况下,用HSDB分析,发现0x00000003跑到了下面,而原来的位置是8B的类型指针
retry:
HeapWord* compare_to = *Universe::heap()->top_addr();
HeapWord* new_top = compare_to + obj_size;
if (new_top <= *Universe::heap()->end_addr()) {
if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
goto retry;
}
result = (oop) compare_to;
}
}
List<MemoryCell *> m_available_table; // 所有可使用的内存
List<MemoryCell *> m_used_table; // 所有已使用的内存
List<MemoryCell *> m_idle_table; // 空闲内存
List<MemoryCell *> m_transer_table; // 待交换内存
#pragma once
using namespace std;
class MemoryPool {
/**
* 所有需要释放内存的成员
*/
private: list<MemoryChunk *> m_chunks;
public: ~MemoryPool();
public: /**
public: /**
打印所有的Chunk */ void print_chunks();
/**
- common.h
1、下面代码可以实现8字节对齐
```cpp
#pragma once
typedef void * pvoid; typedef unsigned char byte; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong;
typedef union { long l_dummy; double d_dummy; void * p_dummy; }Align;
- memory_chunk.h
1、MemoryChunk 直接持有内存
2、8字节对齐,如果申请79B内存,会自动分配80B
3、将分配到的内存分块处理,块是MemoryCell
```cpp
#pragma once
#include "../common.h"
#include "memory_cell.h"
#include <list>
using namespace std;
class MemoryChunk {
private:
/**
* 创建Chunk的文件名
*/
char *m_filename;
/**
* 创建Chunk的文件位置
*/
uint m_line;
/**
* 该Chunk的内存大小
*/
uint m_size;
/**
* 以多少字节对齐
*/
uint m_align_size;
/**
* 该Chunk包含多少Cell
*/
uint m_cell_num;
/**
* 被用了的Cell数量
*/
uint m_used_cell_num;
/**
* 当前Cell的起始位置
* 复制算法用
*/
uint m_cell_start;
/**
* 需要释放内存的数据
*/
private:
/**
* 存储数据的地方
*/
pvoid m_data;
list<MemoryCell *> m_available_table;
list<MemoryCell *> m_used_table;
/**
* 空闲的内存
* 复制算法用
*/
list<MemoryCell *> m_idle_table;
/**
* 整理内存时中转用,暂存打了标记的对象,对象移动后释放
*/
list<MemoryCell *> m_transer_table;
private:
MemoryCell *real_malloc(MemoryCell *available_cell, uint cell_num);
public:
MemoryChunk(uint size, char *filename, uint line);
~MemoryChunk();
public:
char *get_filename();
uint get_line();
uint get_size();
pvoid get_data();
uint get_align_size();
uint get_cell_num();
MemoryChunk *inc_used_cell_num(uint step);
MemoryChunk *desc_used_cell_num(uint step);
list<MemoryCell *> *get_available_table();
list<MemoryCell *> *get_used_table();
list<MemoryCell *> *get_transer_table();
list<MemoryCell *> *get_idle_table();
uint get_cell_start();
MemoryChunk *get_cell_start(uint val);
uint get_new_cell_start();
uint get_old_cell_start();
MemoryChunk *renew_cell_start();
MemoryChunk *set_available_table(list<MemoryCell *> &table);
MemoryChunk *set_used_cell_num(uint val);
public:
MemoryCell *malloc(uint size);
/* 标准-整理算法运行后分配内存 */
MemoryCell *malloc_after_gc(MemoryCell *transer_cell);
MemoryCell *malloc_after_mark_copy_gc(MemoryCell *used_cell);
void free_available_table();
void free_used_table();
public:
void to_string();
void print_available_table();
void print_used_table();
void print_transer_table();
void print_idle_table();
void print_all_table();
};
#pragma once
using namespace std;
class MemoryCell { private: uint m_start; uint m_end;
/**
* Cell的数量,每个Cell占8字节
*/
uint m_size;
bool m_mark;
/**
* 是否是中转对象
*
* 在GC标记阶段会将原对象放入Chunk的transer_table中(因为之前malloc返回的指针指向的是这个对象,这个对象释放了旧指针就失效了)
* 然后生成一个克隆对象放入Chunk的used_table中用于后续的内存释放与整理(如果是多线程,GC阶段需要STW,否则内存的数据会被其他线程覆盖掉)
* 这个属性就是标识是否是这个克隆对象
*/
bool m_transer_object;
private: /**
public: MemoryCell(uint start, uint size); MemoryCell(MemoryCell &cell); ~MemoryCell();
public: uint get_start(); MemoryCell *set_start(uint val);
uint get_end();
MemoryCell *set_end(uint val);
uint get_size();
MemoryCell *set_size(uint val);
bool get_mark();
MemoryCell *set_mark(bool val);
pvoid get_belong_chunk();
MemoryCell *set_belong_chunk(pvoid chunk);
bool get_transer_object();
MemoryCell *set_transer_object(bool val);
/**
* m_start已step为步长递增
* @param step
* @return
*/
MemoryCell *inc_start(uint step);
MemoryCell *desc_start(uint step);
MemoryCell *inc_end(uint step);
MemoryCell *desc_end(uint step);
MemoryCell *inc_size(uint step);
MemoryCell *desc_size(uint step);
public: void to_string(); void to_string(char *msg);
pvoid ptr();
};
- 操作系统也有内存模型
1、堆(OS堆)
2、栈
3、静态区域
4、代码区域 可读可写可执行
- memory_poll.cpp
1、new_chunk向操作系统申请内存
2、print_chunks调试用
3、free_chunks释放内存
```cpp
#include "../include/memory_pool.h"
#include "../include/memory_chunk.h"
MemoryPool::~MemoryPool()
{
INFO_PRINT("[调用析构函数]%s\n", __func__);
free_chunks();
}
//=====
MemoryChunk *MemoryPool::new_chunk(uint mem_size)
{
MemoryChunk *mem_chunk = new MemoryChunk(mem_size, __FILE__, __LINE__);
this->m_chunks.push_front(mem_chunk);
return mem_chunk;
}
//=====
void MemoryPool::print_chunks()
{
INFO_PRINT("[打印未释放的内存]开始\n");
list<MemoryChunk *>::iterator iterator;
for (iterator = m_chunks.begin(); iterator != m_chunks.end(); iterator++) {
MemoryChunk *chunk = *iterator;
INFO_PRINT("\t [未释放的内存]申请位置:( %s:%d ), 内存大小:%d 字节\n",
chunk->get_filename(), chunk->get_line(), chunk->get_size());
}
INFO_PRINT("[打印未释放的内存]结束\n");
}
void MemoryPool::free_chunks()
{
list<MemoryChunk *>::iterator iterator;
for (iterator = m_chunks.begin(); iterator != m_chunks.end(); iterator++) {
delete (*iterator);
}
//TODO 网上说clear时会调用每个元素的析构函数,这里测试发现并不会
m_chunks.clear();
}
#include "../include/common.h"
#include "../include/memory_chunk.h"
MemoryChunk::MemoryChunk(uint size, char filename, uint line): m_size(size), m_filename(filename), m_line(line) { m_align_size = ALIGN_SIZE; m_cell_num = ((size - 1) / m_align_size) + 1; m_size = m_cell_num m_align_size;
this->m_data = calloc(m_size, sizeof(byte));
if (NULL == this->m_data) {
ERROR_PRINT("分配内存失败\n");
exit(1);
}
switch (DEFAULT_GC_TYPE) {
case GC_MARK_CLEAN:
case GC_MARK_COLLOECT:
m_used_cell_num = 0;
m_available_table.push_front(new MemoryCell(0, m_cell_num));
break;
case GC_MARK_COPY:
m_used_cell_num = 0;
m_cell_start = 0;
m_available_table.push_front(new MemoryCell(0, m_cell_num / 2));
m_idel_table.push_front(new MemoryCell(m_cell_num / 2, m_cell_num / 2));
break;
}
print_available_table();
print_idle_table();
}
MemoryChunk::~MemoryChunk() { PRINT("[调用析构函数%s]释放资源\n", func);
if (m_data) {
PRINT("\t 释放资源, 申请内存位置( %s:%d ),内存大小:%d 字节\n", m_filename, m_line, m_size);
free(m_data);
}
free_available_table();
free_used_table();
}
//===== pvoid MemoryChunk::real_malloc(MemoryCell cell, uint cell_num) { / 计算出内存地址 / pvoid ret = (pvoid)((ulong)get_data() + cell->get_start() get_align_size());
INFO_PRINT("[真正分配内存]Data起始地址=%X, cell_start=%d, ret=%X\n", get_data(), cell->get_start(), ret);
/**
* 创建used cell并加入list
*/
m_used_table.push_front(new MemoryCell(cell->get_start(), cell_num));
/**
* 处理Cell信息
*/
cell->inc_start(cell_num)->desc_size(cell_num);
/**
* 更新used_cell_num
*/
inc_used_cell_num(cell_num);
/**
* 如果Chunk用光了,就清空available_table
*/
if (m_cell_num == m_used_cell_num) {
free_available_table();
}
return ret;
}
//===== pvoid MemoryChunk::get_data() { return this->m_data; }
char *MemoryChunk::get_filename() { return m_filename; }
uint MemoryChunk::get_line() { return m_line; }
uint MemoryChunk::get_size() { return m_size; }
uint MemoryChunk::get_align_size() { return m_align_size; }
MemoryChunk *MemoryChunk::inc_used_cell_num(uint step) { m_used_cell_num += step;
if (m_used_cell_num > m_cell_num) {
ERROR_PRINT("cell of chunk size overflow\n");
exit(1);
}
return this;
}
MemoryChunk *MemoryChunk::desc_used_cell_num(uint step) { m_used_cell_num -= step;
if (m_used_cell_num < 0) {
ERROR_PRINT("cell of chunk size overflow\n");
exit(1);
}
return this;
}
//===== pvoid MemoryChunk::malloc(uint size) { pvoid ret = NULL;
if (0 == size) {
ERROR_PRINT("申请的内存大小不得等于0\n");
exit(1);
}
uint cell_num = ((size - 1) / m_align_size) + 1;
if (cell_num > m_cell_num) {
ERROR_PRINT("需要的内存(%d字节)超过最大可用内存(%d字节)\n", size, m_size);
exit(1);
}
/**
* 遍历available_table查找满足条件的MemoryCell
*/
list<MemoryCell *>::iterator available_iterator;
for (available_iterator = m_available_table.begin(); available_iterator != m_available_table.end(); available_iterator++) {
MemoryCell *cell = *available_iterator;
if (cell->get_size() >= cell_num) {
cell->to_string("找到了满足条件的Cell");
ret = real_malloc(cell, cell_num);
}
}
if (NULL == ret) {
ERROR_PRINT("没有满足条件的Chunk,无法分成内存,程序退出\n");
exit(1);
}
print_all_table();
return ret;
}
void MemoryChunk::free_available_table() { PRINT("[释放available_table]开始\n");
list<MemoryCell *>::iterator tmp;
for (tmp = m_available_table.begin(); tmp != m_available_table.end(); tmp++) {
delete (*tmp);
}
m_available_table.clear();
}
void MemoryChunk::free_used_table() { PRINT("[释放used_table]开始\n");
list<MemoryCell *>::iterator tmp;
for (tmp = m_used_table.begin(); tmp != m_used_table.end(); tmp++) {
delete (*tmp);
}
m_available_table.clear();
}
//===== void MemoryChunk::to_string() { PRINT("占 %d 个Cell,占 %d 字节\n", m_cell_num, m_size); }
void MemoryChunk::print_available_table() { PRINT("[打印available_table]开始\n");
list<MemoryCell *>::iterator tmp;
for (tmp = m_available_table.begin(); tmp != m_available_table.end(); tmp++) {
PRINT("\t cell_start=%d, cell_end=%d, cell_size=%d\n", (*tmp)->get_start(), (*tmp)->get_end(), (*tmp)->get_size());
}
PRINT("[打印available_table]结束\n");
}
void MemoryChunk::print_used_table() { PRINT("[打印used_table]开始\n");
list<MemoryCell *>::iterator tmp;
for (tmp = m_used_table.begin(); tmp != m_used_table.end(); tmp++) {
PRINT("\t cell_start=%d, cell_end=%d, cell_size=%d\n", (*tmp)->get_start(), (*tmp)->get_end(), (*tmp)->get_size());
}
PRINT("[打印used_table]结束\n");
}
void MemoryChunk::print_all_table() { print_available_table();
print_used_table();
}
- 垃圾回收算法课上笔记
标记-清除算法 面向整个堆 会产生碎片 如果说你需要分配大对象,需要连续的空间 你的内存是碎片化的,分配不到内存 这个时候不是因为你没有了内存分不到,而是因为你的内存不是连续的 标记-整理算法 内存碎片合并算法(源码展示) 老年代基于这个算法实现的 能合并内存 耗CPU Eden区 对象通常在第一次gc就会被释放,生命周期很短 碎片很多 合并碎片的时候需要STW 暂停所有用户线程 分代+复制算法 JVM中8:1:1 这个算法存在的目的:解决标记-整理算法合并碎片消耗性能过高、gc停止用户线程过长的问题 将内存五五分,一半用(0-10,From),一半空闲(11-20,To) 发生gc的时候,切换角色 一半空闲(11-20,From) 一半用(0-10,To)
- 卡表和记忆集
这一块看一下《深入理解JAVA虚拟机》第三版
简介
本节课内容 1、垃圾判断算法 2、源码及讲解内存池 3、源码及讲解标记清除、标记整理、分代复制算法 4、三色标记、读写屏障、原始快照、增量更新 5、答疑环节
总结