Com relação ao bloqueio de polarização, incluindo desfazer e redirecionar, já analisamos o código-fonte antes, então não vou entrar em detalhes aqui, e principalmente olhar para a questão do atraso na entrada em vigor. Além disso, a maior parte do código-fonte postado neste artigo mantém apenas a lógica da preocupação atual.
Sabemos que a máquina virtual nos fornece o parâmetro-XX: + UseBIASLocking para ligar ou desligar a otimização do bloqueio de polarização (o padrão é ativado), mas a ativação do bloqueio de polarização tem um tempo de atraso padrão, que pode ser definido pelo parâmetro-XX: BiasLockingStartupDelay, o padrão é 4 segundos, você pode encontrar a configuração padrão em globals.hpp:
product(intx, BiasedLockingStartupDelay, 4000,"Number of milliseconds to wait before enabling biased locking")
Este artigo irá para o nível do código-fonte para ver como esse atraso é implementado. Sem muita bobagem, vamos direto ao tópico ~
Análise de código fonte
Digite bIASLocking.cpp para ver a lógica de inicialização do bloqueio polarizado:
void BiasedLocking::init() {
if (UseBiasedLocking) {
//启用偏向锁
if (BiasedLockingStartupDelay > 0) {
//需要延时生效,默认4000ms
EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay);
//调用enroll注册任务
task->enroll();
} else {
//否则立即调用VM_EnableBiasedLocking 开启偏向锁
VM_EnableBiasedLocking op(false);
VMThread::execute(&op);
}
}
}
Em primeiro lugar, UseBIASLocking é verdadeiro, indicando que o bloqueio tendencioso está ativado. Se BiasLockingStartupDelay for maior que 0, significa que um atraso é necessário para entrar em vigor. Aqui está um EnableBisedLockingTask, vamos dar uma olhada na definição desta tarefa:
class EnableBiasedLockingTask : public PeriodicTask {
public:
EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {
}
//这个方法这时先不管,后面调用的时候再说
virtual void task() {
VM_EnableBiasedLocking *op = new VM_EnableBiasedLocking(true);
VMThread::execute(op);
}
};
Pode ser visto que EnableBisedLockingTask é uma classe derivada de PeriodicTask. O construtor de parâmetro da classe pai é chamado com o parâmetro interval_time. PeriodicTask é definido em task.hpp, e o construtor é implementado em task.cpp:
PeriodicTask::PeriodicTask(size_t interval_time) :
_counter(0), _interval((int) interval_time) {
}
O interval_time é atribuído ao atributo _interval, _interval é uma constante privada, em milissegundos, e _counter é definido como 0:
class PeriodicTask: public CHeapObj<mtInternal> {
private:
int _counter;
const int _interval;
}
Até agora, um EnableBulationsLockingTask (uma classe derivada de PeriodicTask) foi criado. Depois de criado, o método task-> matricula () é chamado, o qual é definido na classe pai PeriodicTask:
void enroll();
Encontre a implementação deste método em task.cpp:
void PeriodicTask::enroll() {
MutexLockerEx ml(PeriodicTask_lock->owned_by_self() ?
NULL : PeriodicTask_lock);
_tasks[_num_tasks++] = this;
WatcherThread* thread = WatcherThread::watcher_thread();
if (thread) {
thread->unpark();
} else {
WatcherThread::start();
}
}
Pode ser visto que um objeto WatcherThread é finalmente usado. WatcherThread é uma classe derivada do objeto Thread. É definido em thread.hpp. Nenhum código será postado aqui, caso contrário, ele irá divagar ~ Vamos diretamente para WatcherThread :: run (). Veja, basta postar duas linhas de código:
void WatcherThread::run() {
......
int time_waited = sleep();
......
PeriodicTask::real_time_tick(time_waited);
......
}
Então, vamos dar uma olhada na implementação do método PeriodicTask :: real_time_tick (time_waited), onde o código principal é:
for(int index = 0; index < _num_tasks; index++) {
_tasks[index]->execute_if_pending(delay_time);
}
Anteriormente, chamamos o método PeriodicTask :: register () para registrar o EnableBIASLockingTask na matriz _tasks. Aqui, percorreremos as tarefas de _tasks e chamaremos o método execute_if_pending respectivamente, que é definido na classe pai PeriodicTask:
void execute_if_pending(int delay_time) {
// make sure we don't overflow
jlong tmp = (jlong) _counter + (jlong) delay_time;
if (tmp >= (jlong) _interval) {
_counter = 0;
task();
} else {
_counter += delay_time;
}
}
Quando o julgamento atingir o tempo _interval, o método task () será chamado. O método task () é definido no método EnableBIASLockingTask, então vamos dar a volta e retornar para EnableBIASLockingTask:
class EnableBiasedLockingTask : public PeriodicTask {
public:
EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {
}
virtual void task() {
//实际上是一个VM_Operation
VM_EnableBiasedLocking *op = new VM_EnableBiasedLocking(true);
VMThread::execute(op);
}
};
A lógica do método de tarefa é relativamente simples: primeiro, um objeto VM_EnableBIASLocking é gerado. É uma classe derivada da classe VM_Operation , que implementa a lógica do método doit , e executa esta operação através de VMThread :: execute (se a configuração não tiver efeito com atraso, esta lógica será executada quando o bloqueio de polarização for inicializado) e, finalmente, o método doit ():
class VM_EnableBiasedLocking: public VM_Operation {
public:
void doit() {
SystemDictionary::classes_do(enable_biased_locking);
_biased_locking_enabled = true;
}
};
O método doit chama principalmente o método SystemDictionary :: classes_do. Voltaremos mais tarde para ver o que é enale_bias_locking. Agora vamos ver o que o método SystemDictionary :: classes_do faz:
void SystemDictionary::classes_do(void f(Klass*)) {
dictionary()->classes_do(f);
}
É muito claro aqui, classes_do recebe um ponteiro de função, a função dictionary () retornará as classes carregadas e essas classes carregadas serão armazenadas nos membros estáticos de SystemDictionary na forma de Dictionary:
static Dictionary* _dictionary;
Este dicionário é uma classe derivada de TwoOopHashtable :
class Dictionary : public TwoOopHashtable<Klass*, mtClass> {
}
Aqui, não precisamos nos preocupar muito com essa estrutura de dados, só precisamos saber que a função dictionary () retornará as classes carregadas e, em seguida, chamará as funções passadas nessas classes. Você pode observar a implementação do função classes_do, que é implementada em dictionary.cpp:
void Dictionary::classes_do(void f(Klass*)) {
for (int index = 0; index < table_size(); index++) {
for (DictionaryEntry* probe = bucket(index);
probe != NULL;
probe = probe->next()) {
Klass* k = probe->klass();
if (probe->loader_data() == InstanceKlass::cast(k)->class_loader_data()) {
f(k);
}
}
}
}
É para percorrer todas as classes carregadas, chamar a função passada e passar cada Klass percorrida como um parâmetro. Neste ponto, podemos voltar ao método doit () de VM_EnableBIASLocking, ou seja, dar uma olhada nesta linha de código:
SystemDictionary::classes_do(enable_biased_locking);
Após a análise anterior, sabemos que SystemDictionary :: classes_do receberá um ponteiro de função, percorrerá todas as classes carregadas e, em seguida, chamará a função com a classe (Klass) como parâmetro de entrada. Então, esse enable_bias_locking deve ser um ponteiro de função. Na verdade, voltamos a biaseLocking.cpp para encontrar a definição desta função:
static void enable_biased_locking(Klass* k) {
k->set_prototype_header(markOopDesc::biased_locking_prototype());
}
Rastreie aqui, tudo ficará claro. Há uma propriedade markOop type _prototype_header no Klass, que pode ser usada para indicar a ativação / desativação do bloqueio de polarização do objeto, que é definido em Klass.hpp:
class Klass : public Metadata {
protected:
markOop _prototype_header;
}
Já que o método set_prototype_header () é um método embutido, a implementação específica está em klass.inline.hpp:
inline void Klass::set_prototype_header(markOop header) {
_prototype_header = header;
}
Como você pode ver, a propriedade _prototype_header é definida para o markOop especificado, então o que exatamente é esse markOop agora? Não sei se alguns amigos já têm um sentimento familiar ~ Volte para o método enable_bIAS_locking de biaseLocking.cpp novamente:
static void enable_biased_locking(Klass* k) {
k->set_prototype_header(markOopDesc::biased_locking_prototype());
}
Vamos dar uma olhada no markOop específico retornado por markOopDesc :: bIAS_locking_prototype (). Vá para markOop.h e encontre o método bised_locking_prototype:
static markOop biased_locking_prototype() {
return markOop( biased_lock_pattern );
}
Veja a definição de bised_lock_pattern:
enum {
locked_value = 0,
unlocked_value = 1,
monitor_value = 2,
marked_value = 3,
biased_lock_pattern = 5
};
Isso parece familiar? Se você não estiver familiarizado, deixe-me adicionar uma observação e olhar novamente:
enum {
locked_value = 0,//00 轻量级锁
unlocked_value = 1,//01 无锁
monitor_value = 2,//10 重量级锁
marked_value = 3,//11 GC标记
biased_lock_pattern = 5 //101 偏向锁,1位偏向标记和2位状态标记(01)
};
Isso mesmo, esta definição é o identificador de status de bloqueio no Mark Word do cabeçalho do objeto.
Resumindo
Então, por que a máquina virtual adiciona um controle efetivo de atraso padrão ao bloqueio de polarização? A máquina virtual também iniciará alguns threads durante o processo de inicialização e parte da lógica também é controlada internamente. Se o bloqueio de polarização estiver ativado diretamente, geralmente causará a revogação de polarização. A JVM usa muito ao ativar a revogação de polarização . Para ser seguro, não habilitar o bloqueio de polarização no início é mais útil para melhorar a velocidade de inicialização da JVM (parece que isso deveria ser o (╯ □ ╰) o).
Este artigo é baseado no entendimento pessoal do blogueiro. Se houver algum erro, obrigado por apontá-lo!