$_ is a global variable. Global variables live in symbol tables, and the built-in punctuation variables all live in the symbol table for package main.
You can see the contents of the symbol table for main like this:
$ perl -MData::Dumper -e'print Dumper \%main::' # or \%:: for short
$VAR1 = {
'/' => *{'::/'},
',' => *{'::,'},
'"' => *{'::"'},
'_' => *::_,
# and so on
};
All of the above entries are typeglobs, indicated by the * sigil. A typeglob is like a container with slots for all of the different Perl types (e.g. SCALAR, ARRAY, HASH, CODE).
A typeglob allows you to use different variables with the same identifier (the name after the sigil):
${ *main::foo{SCALAR} } # long way of writing $main::foo
@{ *main::foo{ARRAY} } # long way of writing @main::foo
%{ *main::foo{HASH} } # long way of writing %main::foo
The values of $_, @_, and %_ are all stored in the main symbol table entry with key _. When you access %_, you're actually accessing the HASH slot in the *main::_ typeglob (*::_ for short).
strict 'vars' will normally complain if you try to access a global variable without the fully-qualified name, but punctuation variables are exempt:
use strict;
*main::foo = \'bar'; # assign 'bar' to SCALAR slot
print $main::foo; # prints 'bar'
print $foo; # error: Variable "$foo" is not imported
# Global symbol "$foo" requires explicit package name
print %_; # no error